Conversation
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.
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.
Contributor
There was a problem hiding this comment.
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-Nameheader 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.
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
Collaborator
Author
alitariksahin
approved these changes
Jun 11, 2026
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.