Skip to content

fix(clients): pipe errors (Python + TS), SSE rollback, getTaskStatus timeout#704

Open
anantteotia wants to merge 16 commits intorocketride-org:developfrom
anantteotia:fix/python-send-error-context
Open

fix(clients): pipe errors (Python + TS), SSE rollback, getTaskStatus timeout#704
anantteotia wants to merge 16 commits intorocketride-org:developfrom
anantteotia:fix/python-send-error-context

Conversation

@anantteotia
Copy link
Copy Markdown
Contributor

@anantteotia anantteotia commented Apr 25, 2026

What this does

Improves Python SDK pipe errors for send() / pipe() by raising PipeException with actionable context (common causes + underlying server message) instead of generic RuntimeError, while keeping backward compatibility (PipeException also inherits RuntimeError).

Aligns the TypeScript client: DataPipe open/write/close throws PipeException with the same style of hints; getTaskStatus uses a bounded default wait; connect() ignores blank ROCKETRIDE_APIKEY in the env snapshot so constructor auth is not wiped.

Test harnesses pass a shared default ROCKETRIDE_APIKEY for fork PRs where repo secrets are unavailable (without changing the CI workflow).

Why

Pipe failures were too generic to debug; TS had a half-open risk if SSE subscribe failed after the server opened the pipe; CI could hang or mis-auth when env keys were empty.

How to test

  • Call client.send(...) with an invalid token and verify a PipeException with helpful hints.
  • Call client.send(...) against a chat-source pipeline and verify the error mentions using client.chat().

Notes

Client-side error handling only; no protocol changes.

Notes for reviewers

  • DataPipe / SSE: If setEvents fails after the server assigns a pipe_id, the client best-efforts close() and only sets _opened after SSE setup + callback registration succeed (avoids half-open state). close() can run when a pipe_id exists even if _opened was never set.
  • getTaskStatus: Default per-call timeout 15s; optional second argument options with timeout in ms, or { timeout: false } to omit the per-call override (falls back to normal client request timeout behavior). See CHANGELOG.md [Unreleased].
  • connect / env: Empty or whitespace-only ROCKETRIDE_APIKEY in the client env snapshot is treated as unset (behavior change vs treating "" as a credential); documented in CHANGELOG.

Summary by CodeRabbit

  • Bug Fixes

    • Pipe/data lifecycle now raises a specific PipeException for server-reported failures, improving error handling and compatibility with existing RuntimeError-based catch logic.
    • Failure messages for open/write/close are enriched and standardized, preferring server text and appending a concise “Common causes” checklist.
    • Message content returned to callers is normalized for consistent downstream handling.
  • Documentation

    • Updated in-code docs and throw/exception notes to reflect the refined exception behavior and messaging.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Data pipe lifecycle methods now raise PipeException (which also inherits RuntimeError) for server-reported failures across Python and TypeScript clients; failure payloads/messages are normalized/enriched (including a "Common causes" list for open()), and docstrings/JSDoc updated to reference PipeException.

Changes

Cohort / File(s) Summary
Python DataPipe error handling
packages/client-python/src/rocketride/mixins/data.py
Import PipeException; replace raising plain RuntimeError with PipeException in DataPipe.open(), DataPipe.write(), and DataPipe.close(); normalize/enrich message (use server message or default), copy/mutate response dict to set message before raising; update docstrings to mention PipeException and conditions.
Exception hierarchy (Python)
packages/client-python/src/rocketride/core/exceptions.py
Change PipeException signature to inherit RuntimeError as well (PipeException(RocketRideException, RuntimeError)); extend class docstring with backward-compatibility note referencing pipe-related APIs.
TypeScript DataPipe error handling
packages/client-typescript/src/client/client.ts
Switch thrown errors to PipeException for DataPipe.open/write/close; build/enrich open() failure message (adds "Common causes" bullet list) and include server response in PipeException; update JSDoc @throws annotations.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I nibble logs beneath the moonlight,
Pipes now speak with finer sight.
Messages neat, and reasons found,
I twitch my nose and hop around. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: improving pipe error handling in both Python and TypeScript clients, plus SSE rollback and task status timeout fixes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

No description provided.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/client-python/src/rocketride/mixins/data.py (1)

242-278: ⚠️ Potential issue | 🟡 Minor

Document the new PipeException raised by close().

close() now raises PipeException on server failure (line 278), but its docstring (lines 242-258) has no Raises: section at all. Add one so callers know the close path can throw — relevant because send() at lines 408-420 swallows the original exception only if a follow-up pipe.close() cleanup also fails, but external callers using pipe directly need to know.

📝 Suggested docstring addition
             Returns:
                 Dict: Results from processing the data you sent

+            Raises:
+                PipeException: If the server reports a failure while
+                    finalizing the pipe.
+
             Example:

Also worth aligning the send() docstring at line 375 (RuntimeError: If sending fails) with the new behavior in a follow-up — failures from the underlying pipe will now propagate as PipeException.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client-python/src/rocketride/mixins/data.py` around lines 242 - 278,
Update the docstring for the async method close() to include a Raises: section
that documents PipeException being raised when the server indicates failure
(i.e., after did_fail(response) triggers the PipeException constructed from
response), and mention the method returns PIPELINE_RESULT on success; also
update the send() docstring (method send) to note that underlying pipe failures
now propagate as PipeException instead of only RuntimeError to keep docs
consistent with runtime behavior. Ensure you reference the PipeException symbol
and the close() and send() methods in the docstrings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/client-python/src/rocketride/mixins/data.py`:
- Around line 236-240: Update the write() docstring to accurately list the two
distinct exceptions: keep RuntimeError for the precondition "pipe not opened"
and replace the generic "write fails" RuntimeError wording with PipeException
for write failures; reference the write() method and PipeException class so the
doc clearly documents "Raises: RuntimeError: if pipe not opened" and
"PipeException: if the write operation fails (response indicates failure)".
- Around line 176-187: Update the docstring for the pipe-opening method to list
both RuntimeError (for pre-condition "pipe already opened") and PipeException
(for failures returned by self._client.did_fail) so callers know what to catch,
and change the pre-condition that currently raises RuntimeError('Pipe already
opened') to raise PipeException (or a dedicated PipeAlreadyOpened subclass)
instead so both pre-condition and server-side failures use the same exception
family (reference: PipeException, RocketRideException/DAPException hierarchy,
self._client.did_fail, and the code that sets response['message']).

---

Outside diff comments:
In `@packages/client-python/src/rocketride/mixins/data.py`:
- Around line 242-278: Update the docstring for the async method close() to
include a Raises: section that documents PipeException being raised when the
server indicates failure (i.e., after did_fail(response) triggers the
PipeException constructed from response), and mention the method returns
PIPELINE_RESULT on success; also update the send() docstring (method send) to
note that underlying pipe failures now propagate as PipeException instead of
only RuntimeError to keep docs consistent with runtime behavior. Ensure you
reference the PipeException symbol and the close() and send() methods in the
docstrings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f1083e59-b4c5-44e2-bb14-f93f8493d0e9

📥 Commits

Reviewing files that changed from the base of the PR and between ec6e59e and ce0cd6d.

📒 Files selected for processing (1)
  • packages/client-python/src/rocketride/mixins/data.py

Comment thread packages/client-python/src/rocketride/mixins/data.py
Comment thread packages/client-python/src/rocketride/mixins/data.py
Comment thread packages/client-python/src/rocketride/mixins/data.py
@anantteotia
Copy link
Copy Markdown
Contributor Author

Good catch — agreed this would be a behavior change for users catching only RuntimeError.

I pushed a follow-up commit that preserves backwards compatibility by making PipeException also inherit from RuntimeError (class PipeException(RocketRideException, RuntimeError)), so existing except RuntimeError: handlers keep working while callers can still catch PipeException specifically.

PR updated accordingly.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/client-python/src/rocketride/core/exceptions.py (1)

161-188: 🧹 Nitpick | 🔵 Trivial

LGTM — intentional widening for backward compatibility.

Adding RuntimeError to the bases is consistent with the PR goal: code paths in mixins/data.py (open()/write()/close()) previously raised a bare RuntimeError, so existing except RuntimeError: handlers continue to work after the switch to PipeException. The MRO is valid (PipeException → RocketRideException → DAPException → RuntimeError → Exception) and DAPException.__init__'s super().__init__(error_message) chain remains compatible since RuntimeError.__init__ accepts the same string arg.

One minor nit: the class docstring (lines 162–186) doesn't mention the dual inheritance. Worth a one‑liner so users grepping the exceptions module understand that except RuntimeError will also catch this — and conversely that broad except RuntimeError handlers will now swallow pipe errors too.

📝 Optional docstring tweak
 class PipeException(RocketRideException, RuntimeError):
     """
     Exception raised for data pipe operations.

     Raised when there are problems with data pipes used for sending
     data to pipelines, uploading files, or streaming operations.
+
+    Note:
+        Also inherits from :class:`RuntimeError` for backward compatibility
+        with callers that previously caught ``RuntimeError`` from
+        ``client.send()`` / ``client.pipe()``.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client-python/src/rocketride/core/exceptions.py` around lines 161 -
188, Update the PipeException docstring to explicitly note its dual inheritance
(inherits from RocketRideException and RuntimeError) so users know that "except
RuntimeError:" will also catch PipeException (and that broad RuntimeError
handlers may swallow pipe errors); reference PipeException and the
mixins/data.py methods open()/write()/close() in the sentence so readers can
connect the change that preserved backward compatibility with existing except
RuntimeError handlers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/client-python/src/rocketride/core/exceptions.py`:
- Around line 161-188: Update the PipeException docstring to explicitly note its
dual inheritance (inherits from RocketRideException and RuntimeError) so users
know that "except RuntimeError:" will also catch PipeException (and that broad
RuntimeError handlers may swallow pipe errors); reference PipeException and the
mixins/data.py methods open()/write()/close() in the sentence so readers can
connect the change that preserved backward compatibility with existing except
RuntimeError handlers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d5c337a9-9ca9-42d2-8c5c-9ad4fd23c3b3

📥 Commits

Reviewing files that changed from the base of the PR and between 47b825f and d82aec4.

📒 Files selected for processing (1)
  • packages/client-python/src/rocketride/core/exceptions.py

@anantteotia
Copy link
Copy Markdown
Contributor Author

Addressed the nit: added a short note to the PipeException docstring explaining it also inherits from RuntimeError for backward compatibility with callers catching RuntimeError from client.send() / client.pipe() (and pipe open/write/close).

@stepmikhaylov
Copy link
Copy Markdown
Collaborator

How about applying the same logic to typescript client?

@anantteotia
Copy link
Copy Markdown
Contributor Author

Applied the same improved data-pipe error context to the TypeScript client (DataPipe open/write/close now throw PipeException with the common-causes context on open). Pushed in commit 39e6865 as part of this PR.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/client-typescript/src/client/client.ts (1)

137-155: ⚠️ Potential issue | 🟡 Minor

Wrap the SSE subscription failure path in the same exception type.

open() now converts didFail(response) into PipeException, but the optional setEvents() call at the end of the success path still throws a generic Error. If SSE registration fails, callers lose the new actionable context and the exception type no longer matches the rest of the pipe lifecycle.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client-typescript/src/client/client.ts` around lines 137 - 155,
open() currently converts a failed response into a PipeException but leaves the
SSE registration (this._client.setEvents) to throw a generic Error; wrap the
await this._client.setEvents(...) call in a try/catch and on any error throw a
PipeException instead, preserving useful context (e.g., merge the original
response or construct a response-like object and include the caught error
message) so callers receive a PipeException consistent with the rest of the pipe
lifecycle; update the code paths that set this._client._ssePipeCallbacks only
after successful setEvents, and reference the same symbols: open(),
this._client.setEvents, this._onSSE, this._pipeId, and PipeException.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/client-typescript/src/client/client.ts`:
- Around line 137-155: open() currently converts a failed response into a
PipeException but leaves the SSE registration (this._client.setEvents) to throw
a generic Error; wrap the await this._client.setEvents(...) call in a try/catch
and on any error throw a PipeException instead, preserving useful context (e.g.,
merge the original response or construct a response-like object and include the
caught error message) so callers receive a PipeException consistent with the
rest of the pipe lifecycle; update the code paths that set
this._client._ssePipeCallbacks only after successful setEvents, and reference
the same symbols: open(), this._client.setEvents, this._onSSE, this._pipeId, and
PipeException.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6a2d4bdf-bfac-4910-bfba-2f619958c5a9

📥 Commits

Reviewing files that changed from the base of the PR and between 79fb921 and 39e6865.

📒 Files selected for processing (1)
  • packages/client-typescript/src/client/client.ts

@anantteotia
Copy link
Copy Markdown
Contributor Author

Addressed CodeRabbit TS note: wrapped DataPipe.open() SSE subscription (setEvents) failures to throw PipeException (and only register _ssePipeCallbacks after successful setEvents). Pushed in commit 949a595.

@stepmikhaylov
Copy link
Copy Markdown
Collaborator

@anantteotia many thanks for the update. It's almost fine and only needs conflict resolution.

@anantteotia
Copy link
Copy Markdown
Contributor Author

Resolved merge conflicts against latest develop and pushed updated branch (merge commit 64a9451). PR should be mergeable now.

@anantteotia anantteotia requested a review from kwit75 as a code owner April 29, 2026 20:39
@anantteotia
Copy link
Copy Markdown
Contributor Author

CI failures were due to ROCKETRIDE_APIKEY secret being unavailable on fork PRs (tests boot local server requiring shared key). Added a fallback to 'MYAPIKEY' in .github/workflows/_build.yaml so fork PR CI can pass. Commit 7cff80e.

@github-actions github-actions Bot added the ci/cd CI/CD and build system label Apr 29, 2026
@anantteotia
Copy link
Copy Markdown
Contributor Author

Ubuntu CI failed because \getTaskStatus()\ could hang indefinitely (no per-request timeout), causing \Pipeline Operations → should get pipeline status\ to hit Jest's timeout under parallel CI load.\n\nFix:\n- \getTaskStatus\ now passes \ imeout: 15000\ to \call()/request()\n- Integration test uses \TEST_CONFIG.timeout\ + slightly more retries\n\nPushed: a6671f6

@stepmikhaylov stepmikhaylov enabled auto-merge (squash) April 30, 2026 14:21
Comment thread .github/workflows/_build.yaml Outdated
auto-merge was automatically disabled April 30, 2026 19:10

Head branch was pushed to by a user without write access

@anantteotia
Copy link
Copy Markdown
Contributor Author

Reverted the CI workflow change as requested (removed the fork-PR ROCKETRIDE_APIKEY fallback). Commit: 495dba5. If you'd like, I can open a separate PR to address fork-PR secret unavailability in CI in a safer/approved way.

@github-actions github-actions Bot removed the ci/cd CI/CD and build system label Apr 30, 2026
@anantteotia
Copy link
Copy Markdown
Contributor Author

@stepmikhaylov The CI/CD workflow change has been reverted (commit 495dba5). Once current CI finishes, could you please re-review / mark the ‘changes requested’ as resolved? Thanks!

@anantteotia
Copy link
Copy Markdown
Contributor Author

CI is failing on fork PRs with AuthenticationException: Not authenticated because ROCKETRIDE_APIKEY isn't set when the local test server is spawned. Per request to avoid CI/CD workflow edits, I fixed this in the test harness instead: client-python and client-typescript test tasks now pass env ROCKETRIDE_APIKEY=(existing env or 'MYAPIKEY') into startServer(). Commit: 63fea26.

@anantteotia
Copy link
Copy Markdown
Contributor Author

CI run 25185191220 shows ROCKETRIDE_APIKEY is set to an empty string in the job env, and connect() was using nullish-coalescing (??), so it preferred the blank env key over the constructor auth, sending an empty credential and causing AuthenticationException: Not authenticated.\n\nFix: treat blank ROCKETRIDE_APIKEY in env snapshot as unset, so connect() falls back to this._apikey. Commit: b120203.

@anantteotia
Copy link
Copy Markdown
Contributor Author

CI run 25186225146 still failing on python client tests with AuthenticationException because ROCKETRIDE_APIKEY is present-but-empty in CI env; pytest uses os.getenv('ROCKETRIDE_APIKEY', 'MYAPIKEY') (empty string does not fall back). Fixed by forcing ROCKETRIDE_APIKEY=process.env.ROCKETRIDE_APIKEY||'MYAPIKEY' in client-python test task env before running pytest. Commit: 6ba2b13.

@asclearuc asclearuc changed the title fix(python-client): improve send() pipe error context fix(python-client, typescript-client): improve send() pipe error context May 4, 2026
Copy link
Copy Markdown
Collaborator

@asclearuc asclearuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @anantteotia — direction is right and the typed PipeException upgrade is a clear win.

Requesting changes — two blocking items (a half-open pipe state leak in TS open() and a PR-title scope mismatch), plus a handful of smaller things.

See inline comments for per-line detail and suggested edits.

Comment thread packages/client-typescript/src/client/client.ts
Comment thread packages/client-typescript/src/client/client.ts Outdated
Comment thread packages/client-typescript/src/client/client.ts
Anant Teotia and others added 2 commits May 4, 2026 17:06
- DataPipe: set _opened only after successful SSE setup; rollback via close() on setEvents failure; close() when server assigned pipe_id even if not marked opened

- getTaskStatus: default 15s per-call timeout; optional override or { timeout: false }

- connect: ignore blank ROCKETRIDE_APIKEY in env snapshot (documented)

- Changelog: note TS client behavior changes for upgraders

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions github-actions Bot added the docs Documentation label May 4, 2026
Co-authored-by: Cursor <cursoragent@cursor.com>
@anantteotia anantteotia changed the title fix(python-client, typescript-client): improve send() pipe error context fix(clients): pipe errors (Python + TS), SSE rollback, getTaskStatus timeout May 4, 2026
@anantteotia
Copy link
Copy Markdown
Contributor Author

@asclearuc @stepmikhaylov CI is green on the latest commits. Review items are implemented (DataPipe SSE rollback + \close()\ after partial open, \getTaskStatus\ timeout API with default 15s and { timeout: false }, blank \ROCKETRIDE_APIKEY\ handling). I updated the PR title and description (including the env-var behavior note) and CHANGELOG [Unreleased]. Please re-review when you can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

builder docs Documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants