Repository navigation
fix(clients): keep stats() and Retry-After inside the error contract - #2116
Merged
Merged
Conversation
Contributor
|
Claude Code Review — skipped: draft PR |
L-237: stats(), added after #2013 and #2014, read its body without the checks the other methods have. In Python a 2xx page that is not JSON (a captive portal, a proxy) escaped as a bare JSONDecodeError, and a JSON body that is not an object as AttributeError, though the README says catching CauraError covers HTTP failures. In TypeScript a body that is not an object came back as zero counters. Both now raise "stats response must be a JSON object", as search() and recall() do. L-238: retry_after could hold a delay nobody can wait on. Python's float() takes "nan", "inf", "-5" and digit strings past float range; time.sleep() raises on NaN and negatives and overflows on infinity, and asyncio.sleep(inf) never returns. TypeScript's Number() read "-5" as -5 and a blank header as 0. Both now report None/null unless the delay is finite and non-negative, as the collaboration client's own parser already drops negatives. Signed-off-by: eldad-caura <eldad@caura.ai> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eldad-caura-ai
force-pushed
the
fix/client-stats-retry-after
branch
from
October 20, 2026 10:00
944953f to
7462ee1
Compare
eldad-caura-ai
marked this pull request as ready for review
October 20, 2026 10:00
Contributor
Claude Code Review ✅ No issues found. Reviewed by |
Member
Author
|
@erni-a pleas rparprove |
erni-a
approved these changes
Oct 20, 2026
Member
Author
|
@erni-a please review |
erni-a
approved these changes
Oct 20, 2026
Merged
eldad-caura-ai
pushed a commit
that referenced
this pull request
Oct 20, 2026
🤖 I have created a release *beep* *boop* --- <details><summary>backend: 3.34.6</summary> ## [3.34.6](backend-v3.34.5...backend-v3.34.6) (2026-10-20) ### Bug Fixes * **clients:** keep stats() and Retry-After inside the error contract ([#2116](#2116)) ([f796a6c](f796a6c)) * **collaboration:** install the join client from main, not a deleted branch ([#2111](#2111)) ([8d439c3](8d439c3)) * **field:** bound memory-map builds and see edits in its cache ([#2110](#2110)) ([c222908](c222908)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Signed-off-by: release-please[bot] <release-please[bot]@users.noreply.github.com> Co-authored-by: caura-deploy-bot[bot] <265395343+caura-deploy-bot[bot]@users.noreply.github.com>
1 task
eldad-caura-ai
added a commit
that referenced
this pull request
Oct 20, 2026
## Summary Bumps both SDK clients to **1.1.1** so the next publish carries #2116, the only client change since 1.1.0. The 1.1.0 tags are `caura-client-v1.1.0` and `caura-client-ts-v1.1.0` at fd1da78. Versions only: - **Python:** `clients/python/pyproject.toml` and `caura_client/_version.py`. - **TypeScript:** `clients/typescript/package.json`, `package-lock.json` (its own two entries) and `src/version.ts`. ## What 1.1.1 carries #2116, in both clients: - **L-237:** `stats()` rejects a 2xx body that is not JSON, or not a JSON object, with `CauraAPIError` / `CauraApiError`, as `search()` and `recall()` do. Before, Python leaked `JSONDecodeError` or `AttributeError`, and TypeScript returned zero counters. - **L-238:** `RateLimitError.retry_after` is `None`/`null` unless the delay is finite and non-negative. Before, Python passed through `nan`, `inf` and negatives, and TypeScript passed negatives and read a blank header as 0. **Why a patch:** fixes only, with no new API. A caller only sees a difference for a malformed response or header. ## Release, after merge Each tag runs its publish workflow, which checks that the tag's version matches the package: - `caura-client-v1.1.1` → `publish-python-client.yml` (PyPI `caura-client`) - `caura-client-ts-v1.1.1` → `publish-npm-client.yml` (npm `@caura/client`) The wrapper packages depend on `caura-client>=1.0.0` (`caura`, `caura-sdk`), `caura-client>=1.0.3` (the memclaw shim) and `@caura/client ^1.0.0`/`^1.0.1` (npm), so they pick up 1.1.1 without a release of their own. ## Test plan - [ ] CI: the Python client's version test (`__version__` agrees with `pyproject.toml`), and the TypeScript client's build and tests, pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: eldad-caura <eldad@caura.ai> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two Low findings in the client SDKs, from the security audit's review of the client changes merged since its last recheck. Both are in the clients' response handling, so they share one PR.
L-237:
stats()skips the response checks every other method has. It was added in #2035, after #2013 and #2014 made a non-JSON 2xx body aCauraAPIError.stats()readsresponse.json()directly. A captive-portal or proxy page with a 200 status escapes as a bareJSONDecodeError, although the README says catchingCauraErrorcovers HTTP failures. A JSON body that isn't an object raisesAttributeError.stats()returns zero counters with a non-objectrawfor a body that isn't an object.search()andrecall()in both clients reject such bodies with"... response must be a JSON object".L-238:
retry_aftercan hold a value nobody can wait on.float()acceptsnan,inf,-5, and digit strings past float range (which becomeinf). A caller that sleeps on it fails:time.sleep()raises for NaN and negatives and overflows on infinity, andasyncio.sleep(inf)never returns.Number()reads-5as -5 and a blank header as 0.The collaboration client's own parser (
caura_bus_core.retry.retry_after_seconds) already rejects negatives.Fix
stats()rejects a body that is not a JSON object with"stats response must be a JSON object", the same checksearch()andrecall()make.stats()reads its body through_json(), the helper from fix(client-python): raise CauraAPIError for non-JSON 2xx bodies #2013, so a 2xx page that is not JSON raisesCauraAPIErrorlike every other call.retry_afteris kept only when it is finite and non-negative; otherwise it isNone/null. TypeScript also treats a blank header as absent. Valid delays and HTTP-dates (past dates still give 0) are unchanged.Test plan
statsin the non-JSON 2xx test (JSONDecodeError), the three non-objectstatsbodies (AttributeError), andretry_afterfor-5,nan,infand a 400-digit value (-5.0,nan,inf,inf).statsbodies (resolved instead of rejecting), and-5and a blank header (-5and0instead ofnull).🤖 Generated with Claude Code