Skip to content

Fix max_retries=0, wait timeouts, run_many retries and partial failures, input encoding - #89

Open
jadenfix wants to merge 8 commits into
roe-ai:mainfrom
jadenfix:fix/correctness
Open

jadenfix wants to merge 8 commits into
roe-ai:mainfrom
jadenfix:fix/correctness

Conversation

@jadenfix

@jadenfix jadenfix commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Small, independent fixes, one commit each so any can be dropped:

  • max_retries=0 was ignored. RoeConfig.from_env used max_retries or <env>, so 0 fell back to ROE_MAX_RETRIES / 3.
  • Job.wait / JobBatch.wait overran the timeout. They always slept a full interval after the deadline check, so wait(interval=60, timeout=1) raised after 60s. Now they sleep at most until the deadline and use time.monotonic().
  • FileUpload(path=...) agent inputs leaked a file descriptor. The handle opened by to_multipart_tuple() was never closed; read the bytes in a with block like plain path strings already are.
  • User-Agent said roe-python/0.1.0. Now uses roe.__version__.
  • run_many chunks were retried. The transport retried the batch POST on 5xx/408/429, so one chunk could create (and bill) its jobs up to 4 times. It now sends x-roe-skip-retry like the single-run helpers.
  • run_many lost job IDs when a later chunk failed. The exception now carries submitted_job_ids for the jobs earlier chunks already started.
  • Dict and list agent inputs were sent as Python repr ({'a': 'b'}). They're now JSON-encoded; other types are unchanged.
  • JobBatch.wait hung until its timeout (2h by default) when the status response left out a job (e.g. a deleted ID). It now raises NotFoundError naming the missing IDs on the first poll, like the Go SDK does.

Testing

  • New tests: test_config.py, test_job_wait.py (fake clock), test_inputs.py, test_auth.py, plus asserts in test_agents_wrapper_transport.py. Each fails before its fix and passes after.
  • uv run pytest, ruff check, ruff format --check.
  • New test files are added with git add -f because .gitignore contains tests/.

Testing against the live API requires a Roe API key, so these were verified with unit tests and local mock servers only.

Fixes #87. Part of #85 (only run_many; the create/upload POSTs are unchanged) and #86 (only dict/list encoding; plain-string file-path detection needs your call).

`max_retries or <env>` treated 0 as "not provided", so passing
max_retries=0 fell back to ROE_MAX_RETRIES or the default 3 and
retries could not be turned off from code.

Tested: new tests/unit/test_config.py fails before, passes after;
full suite passes.
Both loops checked the deadline and then always slept a full
`interval`, so wait(interval=60, timeout=1) raised after 60s. Sleep
at most until the deadline, and use time.monotonic() so wall-clock
changes don't affect the timeout.

Tested: tests/unit/test_job_wait.py (fake clock) fails before with
60s elapsed, passes after; full suite passes.
build_execution_multipart passed the handle from
FileUpload.to_multipart_tuple() to httpx, and nothing closed it, so
every run with a path-based FileUpload leaked a file descriptor. Read
the bytes inside a `with` block, the same way plain path strings are
already handled a few lines below.

Tested: tests/unit/test_inputs.py fails before (open BufferedReader
returned), passes after; full suite passes.
The header was hard-coded to roe-python/0.1.0 while the package is
1.1.15, so server-side logs can't tell SDK versions apart. Use
roe.__version__ (imported lazily to avoid a circular import).

Tested: tests/unit/test_auth.py fails before ('roe-python/0.1.0'),
passes after; full suite passes.
@greptile-apps

greptile-apps Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium impact] Fixes timeout logic, retry config, file handle leak, and User-Agent staleness.

Fix the extra poll after the deadline before merging; the upload memory increase is a non-blocking concern.

Fix All in Claude CodeFindings

  1. P1 Waiting continues after timeout ▶
  2. P2 Uploads hold entire files ▶

Summary

The PR fixes four SDK behaviors and adds focused unit tests:

  • Explicit zero retries stay at zero.
  • Job waits stop sleeping at their deadline.
  • Path-based uploads close their file after reading.
  • API requests use the installed package version.

Waiting can still start another request after its deadline. Path-based uploads also now hold entire files in memory.

Reviews (1) · Last reviewed commit: "Send the package version in the User-Age..." · Reviewed by Greptile

Comment thread src/roe/models/job.py
Comment thread src/roe/utils/inputs.py
retrieve_status_many returns only the jobs the server knows about, and
JobBatch.wait skipped any it didn't get back. A deleted or mistyped job
ID therefore never reached a terminal state, and wait() polled until
its timeout (two hours by default) before raising TimeoutError. Raise
NotFoundError naming the missing IDs on the first poll instead, as the
Go SDK does. IDs are compared in canonical UUID form, so callers who
pass upper-case IDs see no change.

The existing batch timeout test built status items as dicts, whose IDs
JobBatch can't read, so it was passing through this same path; it now
uses real UUIDs and attribute-style items.

Tested: new test in tests/unit/test_job_wait.py fails before
(TimeoutError after the full 60s) and passes after; full suite, ruff
check and ruff format --check pass.
RoeRetryTransport retried the run_async_many POST on 5xx/408/429, which
can submit (and bill) a chunk up to four times. Send x-roe-skip-retry like
the single-run helpers do. Fixes roe-ai#85.
A failure on a later chunk discarded the IDs of jobs already started by
earlier chunks. Attach them to the exception as submitted_job_ids and
re-raise it unchanged. Fixes roe-ai#87.
Non-string inputs went through str(), so {"a": "b"} reached the server as
the Python repr {'a': 'b'}. JSON-encode dicts and lists; other types are
unchanged. Part of roe-ai#86.
@jadenfix jadenfix changed the title Fix max_retries=0, Job.wait timeout overrun, FileUpload handle leak, stale User-Agent Fix max_retries=0, wait timeouts, run_many retries and partial failures, input encoding Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

run_many loses job IDs when a later chunk fails

1 participant