Toss Token 401 Ping-Pong - The Real Root Cause Was a Missing Cache Re-check
Finally tracked down why an external quote API's auth token kept invalidating itself, fixed the re-issue logic, and set up an automated check instead of relying on memory to confirm the fix held.
This is the English version of a post originally written in Korean for my algorithmic trading system devlog(new tab).
The token kept invalidating itself, morning after morning
For the past several days, logs showed the auth token for an external quote API getting invalidated repeatedly, mostly in the early morning hours.
At first I treated it as a one-off caused by two resident processes restarting at slightly different times, and just restarted them again. A few days later the same symptom came back.
It only affected price lookups, not the order path itself, so there was no urgency. But since it kept recurring, I asked another AI tool to read through the actual code and narrow down the real cause.
The real cause: re-issuing a token without checking the cache first
The advisory came back with a different answer than the restart-timing theory. Whenever the API rejected a token (401), the client immediately requested a brand new one — without first checking whether another process had already fetched a fresh one.
Several processes using this API run at the same time. When one gets rejected and issues a new token, that instantly invalidates whatever the other process was holding.
The other process reacts the same way and issues its own new token, which invalidates the first one right back. That's the ping-pong — two processes repeatedly invalidating each other's tokens. The advisory also confirmed the first collision tends to start right around when the token's own expiry window (about a day after issuance) runs out.
Fixed and deployed the same day, with a dedicated check built in
The fix was straightforward in direction: on a 401, don't immediately request a new token. Re-read the stored cache first, retry with it if it had already changed, and only fall back to issuing a new token — under a file lock, with just one process doing it — if that still fails.
The change was implemented and tested in an isolated workspace, reviewed, then merged. After market close, all six resident processes using this API were restarted and came back up clean.
Rather than just declaring it fixed, I built a small dedicated script that tallies invalidation events from the logs. Before the fix, it counted dozens of events in the affected window; after deployment, zero.
Instead of relying on remembering to check back manually, I scheduled this same tally to run automatically on the next couple of trading days and report the result over Telegram. If it recurs, I'll know right away.
Closed out one hypothesis per its pre-registered rule
Separately, a statistical hypothesis test reached its conclusion today. A candidate signal based on supply-demand flow data was tested, following a procedure fixed in advance, to see whether it improved predictive power — and it failed to clear the statistical bar in its first observation window.
Rather than lowering the bar or reshuffling the test window to try again, I closed this hypothesis exactly per the rule set before the test ran. Repeatedly tweaking conditions until you get the result you want tends to produce a "lucky pass" that looks meaningful but isn't — sticking to a pre-committed rule is the safer call even when the immediate result is disappointing.