Skip to content

fix(execution): throw NonRetriableError when step.invoke target is cancelled - #1707

Open
marsyg wants to merge 1 commit into
inngest:mainfrom
marsyg:fix/step-invoke-cancelled-error
Open

fix(execution): throw NonRetriableError when step.invoke target is cancelled#1707
marsyg wants to merge 1 commit into
inngest:mainfrom
marsyg:fix/step-invoke-cancelled-error

Conversation

@marsyg

@marsyg marsyg commented Aug 15, 2026

Copy link
Copy Markdown

Summary

When an invoked child function was cancelled in the Dev UI or via API, step.invoke(...) previously resolved with the internal inngest/function.finished event payload (with data._inngest.status === "Cancelled") instead of rejecting. This broke TypeScript return type assumptions and caused runtime crashes when trying to parse or use the return value.

This PR normalizes memoized step states in engine.ts (normalizeMemoizedOp) so that cancelled invocations set data: undefined and error: { name: "NonRetriableError", message: "Invoked function was cancelled" }. This ensures:

  1. step.invoke(...) rejects with a StepError (NonRetriableError).
  2. Parent functions can catch and handle cancellations cleanly using try { await step.invoke(...) } catch (err).
  3. Uncaught cancellations immediately fail the parent run without wasteful retries.

Checklist

  • Added a docs PR that references this PR N/A Bug fix aligning with existing NonRetriableError step behavior
  • Added unit/integration tests
  • Added changesets if applicable

Related

Fixes #1694

@changeset-bot

changeset-bot Bot commented Aug 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 56a7c82

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
inngest Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@mendral-app mendral-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Needs attention — 1 issue in 1 file

normalizeMemoizedOp applies to all memoized ops, not just invoke steps. A step.run whose user-returned data happens to contain { _inngest: { status: "Cancelled" } } would be incorrectly rewritten to an error. The likelihood is low but the blast radius is silent data corruption for any affected step. The fix should scope the normalization to invoke ops only (checking op.op === "InvokeFunction" or similar discriminant). Otherwise the logic and error shape are correct.

What this PR does

Adds a normalizeMemoizedOp function that intercepts memoized step state for step.invoke results. When the invoked function was cancelled (detected via _inngest.status === "Cancelled" in the event payload), it rewrites the op to have data: undefined and an error with name: "NonRetriableError", causing the step to reject with a StepError instead of resolving with the raw internal event payload.

Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.

<assessment>
`normalizeMemoizedOp` applies to **all** memoized ops, not just invoke steps. A `step.run` whose user-returned data happens to contain `{ _inngest: { status: "Cancelled" } }` would be incorrectly rewritten to an error. The likelihood is low but the blast radius is silent data corruption for any affected step. The fix should scope the normalization to invoke ops only (checking `op.op === "InvokeFunction"` or similar discriminant). Otherwise the logic and error shape are correct.
</assessment>

<file name="packages/inngest/src/components/execution/engine.ts">
<issue location="packages/inngest/src/components/execution/engine.ts:140">
`normalizeMemoizedOp` runs on every step type, but the `_inngest.status` check on the non-event path (lines 152-155) can match user-returned data from `step.run` if it coincidentally contains `{ _inngest: { status: "Cancelled" } }`. This would silently turn a successful step result into an error. Guard the transformation so it only applies to invoke-type ops (e.g. check the op type or limit the fallback status detection to only fire when `isFinishedEvent` is true).
</issue>
</file>

Tag @mendral-app with feedback or questions. View session

Comment thread packages/inngest/src/components/execution/engine.ts
@marsyg

marsyg commented Aug 17, 2026

Copy link
Copy Markdown
Author

Hi @amh4r @scottnuma would you be able to review this change?

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.

[BUG] Cancelled function invoke tasks resolve with wrong type

1 participant