Wave A PR 3: typed errors + 401/429 escalation + body-size + key validation#31
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (24)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Covers issues #2, #3, #8, #13, #14, #15, #17, #18 across three sequenced PRs: foundation (mypy + CI + hygiene), lifecycle & correctness (fork-safety, reentrancy guard), and transport & validation (auth escalation, 429 handling, api_key validation, body-size fixes). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three-PR sequence with bite-sized TDD tasks. PR 1 foundation (CI + mypy + hygiene), PR 2 lifecycle (RecostError base + fork-safety + reentrancy guard), PR 3 transport (typed errors + 401 escalation + 429 deferral + key validation + body sizing). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… dead type-ignores - RawEvent.latency_ms: int → float (latency is naturally fractional) - MetricEntry.total/p50/p95_latency_ms: int → float to match - _Bucket.latencies: List[int] → List[float]; _compute_percentile updated - _patched_urlopen: explicit Any-annotated signature, _UNSET sentinel for lazy timeout resolution, remove dead pool_connections/pool_maxsize kwargs - _patched_send, _patched_async_send, _patched_request: explicit Any signatures - Replace # type: ignore[no-untyped-def] with proper signatures throughout - Replace # type: ignore[attr-defined] with # type: ignore[method-assign] on all method-assignment patching lines (urllib3, httpx, aiohttp) - Remove now-stale # type: ignore[import-untyped] comments (urllib3/aiohttp ship with inline types when installed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace `except Exception as exc:` with bare `except Exception:` in four locations where the exception is immediately re-raised. This preserves the original traceback while satisfying ruff's F841 (unused variable) check. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wave 1 changes brought back unused imports (time/pytest in test_init.py, MAX_BUCKETS in _transport.py) and a stale type-ignore on the typed websockets import. CI now passes on the rebased branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…very Stores a _rebuild callable on RecostHandle that recreates the transport and flush-timer thread in the current PID; supports post-fork recovery. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nit (#3) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
#3) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace single ContextVar guard with dual mechanism: ContextVar for async-task scope, threading.local for same-thread recursion, and a threading.Thread.__init__ patch to propagate the guard ContextVar into child threads so cross-thread double-counting is prevented. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…uard Removes _original_thread_init, _patch_thread(), and _unpatch_thread() which injected a context= kwarg into threading.Thread.__init__ — this kwarg only exists on Python 3.14 and breaks CI on 3.9–3.12. The ContextVar + threading.local dual guard is retained; asyncio.to_thread already propagates contextvars on all supported Python versions (3.9+), which is the actual failure mode from issue #15. Updates test_reentrancy.py to test the asyncio.to_thread path and same-thread recursion guard rather than the raw Thread() path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds validation to reject api_key that isn't a non-empty string beginning with 'rc-'. This catches the common "undefined" env-var typo and other format errors immediately, with a helpful error message pointing to documentation. Test coverage: 4 new tests added to verify string "undefined", non-string types, valid "rc-" prefix, and None api_key are handled correctly.
…ry-After Replaces None/raise API with a structured _CloudResult dataclass carrying status, retry_after_ms, and error. Adds _parse_retry_after() supporting both integer-seconds and HTTP-date formats. Transport.send() updated for behavior parity; 401/429 escalation branches deferred to Tasks 19/20. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…at 5 (#14) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ext flush (#18) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…erators (#8) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dation The Wave 1 atexit subprocess test used api_key="test" which now fails the new init() validation. Update to "rc-test" to satisfy the format check while keeping the test's intent (a fake key so transport.send is exercised against a mocked patched send).
ecc7a2c to
c4f9ba1
Compare
Summary
RecostAuthError; the 5th emitsRecostFatalAuthErrorand suspends the transport. First failure also prints a one-line stderr warning so the developer is told immediately.Retry-Afterheader (seconds or HTTP-date), defers the next flush by that delay, emitsRecostRateLimitError. Window is not dropped.init(api_key="undefined")now raisesValueErrorwith a clear message. Catches the most common misconfig.json=, httpx streaming, Content-Length only #8 body sizes: aiohttpjson=andFormDataare now measured; httpx streaming bodies are explicitly skipped to avoid OOM on large uploads.Public API additions
RecostError(PR 2),RecostAuthError,RecostFatalAuthError,RecostRateLimitError— all subclasses ofException. Existingon_error: Callable[[Exception], None]signature unchanged.Test plan
tests/test_errors.py— new exception classes import and carry expected attrs.tests/test_transport.py— 401 escalation, fatal at 5, 429 deferral, 429 not auth-counted.tests/test_init.py—api_key="undefined"raises.tests/test_interceptor.py— aiohttpjson=/FormData sized, httpx streaming not materialized.Closes #8, #14, #17, #18.
🤖 Generated with Claude Code