Skip to content

DX-2735: report failing step name via Upstash-Error-Step-Name header#201

Merged
CahidArda merged 9 commits into
mainfrom
DX-2735
Jun 11, 2026
Merged

DX-2735: report failing step name via Upstash-Error-Step-Name header#201
CahidArda merged 9 commits into
mainfrom
DX-2735

Conversation

@CahidArda

Copy link
Copy Markdown
Collaborator

When a step throws during execution, attach the step name to the error and surface it on the 500 error response via the Upstash-Error-Step-Name header so Workflow Logs can show which step is being retried.

When a step throws during execution, attach the step name to the error and
surface it on the 500 error response via the Upstash-Error-Step-Name header
so Workflow Logs can show which step is being retried.
@linear-code

linear-code Bot commented Jun 10, 2026

Copy link
Copy Markdown

DX-2735

The server now reports the failing step name (from the Upstash-Error-Step-Name
header) on the "next" step log group. Surface it as an optional stepName field
in the logs response type.

Copilot AI 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.

Pull request overview

This PR adds end-to-end propagation of the failing workflow step name so that, when execution throws, the SDK can return a 500 with an Upstash-Error-Step-Name header for improved observability in Workflow Logs (including partial parallel execution paths).

Changes:

  • Add Upstash-Error-Step-Name header constant and include it on 500 responses when a failing step name is known.
  • Attach the currently executing step name onto thrown errors (including preservation through the partial-parallel re-wrap path).
  • Extend test coverage to assert the header is present for both sequential and parallel-step failures; update requestcatcher test endpoints.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/test-utils.ts Updates requestcatcher base URLs used by tests.
src/serve/serve.test.ts Adds assertions/tests to ensure failing step name is surfaced via response header, including parallel failure.
src/serve/index.ts Reads step name from error and conditionally adds Upstash-Error-Step-Name to 500 responses.
src/qstash/submit-steps.ts Attaches step name to errors when step function throws during submission.
src/error.ts Introduces helper APIs to attach/read a step name on errors.
src/context/auto-executor.ts Preserves failing step name when re-wrapping errors in partial parallel execution.
src/constants.ts Adds WORKFLOW_ERROR_STEP_NAME_HEADER constant.
src/client/types.ts Extends log typing to optionally include stepName when available from failing-step reporting.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/error.ts
Comment thread src/serve/index.ts
DLQMessage only surfaced the first label via the deprecated `label`
field, unlike WorkflowRunLog which already exposes the full `labels`
array. Add `labels: string[]` to DLQMessage (and PublicDLQMessage) and
mark `label` deprecated, matching the run-log type.

Adds a mocked test asserting both fields round-trip through dlq.list()
and a live test confirming a run triggered with label: [a, b] surfaces
label === a and labels === [a, b] on its DLQ message.
- attachStepNameToError no longer throws on non-extensible/frozen errors,
  so it can never mask the original failure
- sanitize the step name (strip control chars like CR/LF) before putting it
  in the Upstash-Error-Step-Name header so an invalid value can't break the
  500 response
@CahidArda

Copy link
Copy Markdown
Collaborator Author

in 928a6fe, added labels to dlq results which was missed in #197

@CahidArda CahidArda merged commit 8cd19e7 into main Jun 11, 2026
22 of 23 checks passed
@CahidArda CahidArda deleted the DX-2735 branch June 11, 2026 11:40
CahidArda added a commit that referenced this pull request Jun 17, 2026
…201)

* feat: report failing step name via Upstash-Error-Step-Name header

When a step throws during execution, attach the step name to the error and
surface it on the 500 error response via the Upstash-Error-Step-Name header
so Workflow Logs can show which step is being retried.

* feat: expose stepName on failed next-step logs

The server now reports the failing step name (from the Upstash-Error-Step-Name
header) on the "next" step log group. Surface it as an optional stepName field
in the logs response type.

* fix: change requestcatcher to httpstatus

* fix: update mock server URLs to use requestcatcher

* feat: expose labels array on DLQ messages

DLQMessage only surfaced the first label via the deprecated `label`
field, unlike WorkflowRunLog which already exposes the full `labels`
array. Add `labels: string[]` to DLQMessage (and PublicDLQMessage) and
mark `label` deprecated, matching the run-log type.

Adds a mocked test asserting both fields round-trip through dlq.list()
and a live test confirming a run triggered with label: [a, b] surfaces
label === a and labels === [a, b] on its DLQ message.

* fix: bump version

* fix: urls in the tests

* fix: harden step-name error annotation and header sanitization

- attachStepNameToError no longer throws on non-extensible/frozen errors,
  so it can never mask the original failure
- sanitize the step name (strip control chars like CR/LF) before putting it
  in the Upstash-Error-Step-Name header so an invalid value can't break the
  500 response

* style: apply prettier formatting fixes to test files
CahidArda added a commit that referenced this pull request Jun 25, 2026
* fix: reject empty id in single-resource endpoints (DX-2769)

* DX-2735: report failing step name via Upstash-Error-Step-Name header (#201)

* feat: report failing step name via Upstash-Error-Step-Name header

When a step throws during execution, attach the step name to the error and
surface it on the 500 error response via the Upstash-Error-Step-Name header
so Workflow Logs can show which step is being retried.

* feat: expose stepName on failed next-step logs

The server now reports the failing step name (from the Upstash-Error-Step-Name
header) on the "next" step log group. Surface it as an optional stepName field
in the logs response type.

* fix: change requestcatcher to httpstatus

* fix: update mock server URLs to use requestcatcher

* feat: expose labels array on DLQ messages

DLQMessage only surfaced the first label via the deprecated `label`
field, unlike WorkflowRunLog which already exposes the full `labels`
array. Add `labels: string[]` to DLQMessage (and PublicDLQMessage) and
mark `label` deprecated, matching the run-log type.

Adds a mocked test asserting both fields round-trip through dlq.list()
and a live test confirming a run triggered with label: [a, b] surfaces
label === a and labels === [a, b] on its DLQ message.

* fix: bump version

* fix: urls in the tests

* fix: harden step-name error annotation and header sanitization

- attachStepNameToError no longer throws on non-extensible/frozen errors,
  so it can never mask the original failure
- sanitize the step name (strip control chars like CR/LF) before putting it
  in the Upstash-Error-Step-Name header so an invalid value can't break the
  500 response

* style: apply prettier formatting fixes to test files

* test: replace requestcatcher.com with centralized example.com constant

requestcatcher.com was failing live deliveries with TLS certificate
errors. Replace it with the IANA-reserved example.com (valid cert,
stable) and centralize the host into a single MOCK_DESTINATION_HOST
constant in test-utils, referenced everywhere instead of repeating
the literal. Examples define a local MOCK_DESTINATION_URL since they
can't import test-utils.

Also switch the triggerWorkflowDelete test from spyOn (which made a
real network call) to the local mockQStashServer utility, removing the
last external request dependency.

* fix: address review comments on empty-id validation (DX-2769)

- Validate every id in array/legacy forms of cancel() and DLQ
  resume/restart/delete via a shared toNonEmptyIdArray helper, not just
  the single-string overload, so cancel([""]) / delete([""]) fail fast
  instead of sending a bulk filter the server treats as a collection op.
- assertNonEmptyId: use a falsy guard instead of id.length so a plain-JS
  caller passing undefined/null gets a consistent QstashError rather than
  a TypeError from reading .length.
- makeNotifyRequest: treat workflowRunId with an explicit undefined check
  and validate it when provided (an empty string no longer silently
  changes the request path).
- await the .rejects assertions inside mockQStashServer execute callbacks
  so the rejection is actually asserted.
- add tests covering an empty string inside an id array for cancel and
  DLQ delete.
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.

3 participants