Repository navigation
Description
Evidence
During the authenticated Senseno remediation on 2026-08-09, validateApiAuthMaterial() intermittently returned network-error even though the same cookie material immediately produced HTTP 200 from https://substack.com/api/v1/handle/options using direct fetch. The local rate-limit state was stored under a OneDrive-backed project directory.
withRateLimit() currently encloses the successful fetch, response accounting, and controller.persist() in one try. A local persistence exception therefore enters the network-error catch path, records status 0, retries persistence, and causes requestJson() to return status 0. This conflates local state persistence with transport failure and can corrupt failure/cooldown evidence.
A separate real HTTP 429 was later observed and correctly stopped the mutation window. These two conditions must remain distinguishable.
Options
A. Typed fail-closed persistence error (recommended)
Separate fetch failure from post-response accounting/persistence. Introduce a typed RateLimitStatePersistenceError and propagate it distinctly; do not overwrite the observed HTTP status with 0. Callers remain fail-closed, but diagnostics say local persistence failed and retain the successful response status in redacted evidence.
Trade-off: safest semantics and auditability, but extends result/error contracts and CLI rendering.
B. Bounded local persistence retry plus typed terminal error
Add a short bounded retry for transient Windows/OneDrive write failures, then use Option A if it still fails.
Trade-off: improves Windows reliability, but needs deterministic tests and must not delay or retry the HTTP request itself.
C. Best-effort persistence
Return the successful HTTP response even when persistence fails and warn.
Trade-off: simplest user experience, but unsafe for mutation workflows because the next process may lose an active cooldown. Not recommended as the default.
Recommendation and fallback
Implement B with A as the terminal behavior. Keep mutation callers fail-closed. If cooldown state cannot be persisted, block further writes and surface a remediation message; read-only callers may expose the successful HTTP status but must mark cooldown persistence as degraded. Do not label either condition as a network failure.
Acceptance criteria
- A successful HTTP response followed by a mocked
writeFilefailure is not reported as status 0/network-error. - The observed HTTP status and redacted rate-limit headers remain available in the error/receipt.
- Persistence retries never repeat the HTTP request.
- Mutation paths fail closed when cooldown state cannot be durably stored.
- Concurrent-process and transient Windows/OneDrive failure tests cover the state file.
- A genuine fetch failure and a genuine HTTP 429 retain their existing distinct behavior.
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Activity
Separate rate-limit persistence failures (#520)