diff --git a/src/service.zig b/src/service.zig index 0dbf7d4..45b300d 100644 --- a/src/service.zig +++ b/src/service.zig @@ -1003,11 +1003,16 @@ pub const DataService = struct { } return DataError.TransientError; } - // NotFound, ParseError, InvalidResponse, AuthError - - // symbol genuinely has no candle data on Tiingo (the only - // provider for historical candles since the 2026-05 - // audit). Negative-cache so we don't keep retrying. - s.writeNegative(symbol, .candles_daily); + // Only a genuine NotFound means "this symbol has no candle + // data on Tiingo" (the sole historical-candle provider since + // the 2026-05 audit) and earns a sticky negative-cache entry. + // Unauthorized / InvalidResponse / PaymentRequired and the + // like are not permanent facts about the symbol (transient + // auth misconfig, a malformed response) - fail this call but + // stay retryable, matching fetchCached's policy. This matters + // now that the candle negative cache is actually honored: a + // bad negative would otherwise stick until --refresh. + if (isPermanentProviderFailure(err)) s.writeNegative(symbol, .candles_daily); return DataError.FetchFailed; }; // populateAllFromTiingo writes all three caches itself; we