Skip to content

Step executor throws when a step error has a non-writable stack, so the step is never retried or failed #4369

Closed

Description

@rpinckne

@workflow/core 5.0.0-beta.55, dist/runtime/step-executor.js. Both the FatalError path and the retry path apply the normalized stack by assignment:

if (types.isNativeError(err) && normalizedStack) {
    err.stack = normalizedStack;
}

If the thrown error's stack is a non-writable data property, that assignment throws a TypeError inside the executor's catch block. As a result:

  • The executor writes neither step_retrying nor step_failed.
  • The queue handler logs Queue handler failed for message ... on delivery attempt 1; retrying in 1s: TypeError: Cannot assign to read only property 'stack' of object '...'.
  • The redelivery finds the step already running, so it stays at attempt 1 until the execution lease expires. On our deployment that was about 14 minutes.

A transient step error becomes a long stall with no step event. Postgres.js errors on Node 22+ are one source of such errors (porsager/postgres#1227). Frozen errors and some third-party errors are others.

Repro

A step that throws:

const e = new Error('transient')
Object.defineProperty(e, 'stack', { value: e.stack, writable: false })
throw e

Expected: the step retries or fails, with the normalized stack in the event payload.
Actual: the queue handler fails and the step stays running until its lease expires.

Suggested fix

Don't mutate the caller's error. Either pass normalizedStack to the dehydrator, or guard the assignment (a try/catch, or Object.defineProperty when the property is configurable). In the published dist, the FatalError site is around line 1023 and the retry site around line 1131.

Activity

  1. rpinckne commented on Oct 13, 2026

    @rpinckne
    Author

    Fixed by #4549, which shipped in @workflow/core 5.0.1. On 5.2.1 a step that throws an error with a read-only stack now records step_retrying and retries as expected, so we removed our workaround. Closing. Thanks!

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions