Running Steps Inside try/catch Blocks
When running steps (run, sleep, call, or any other step) inside a try/catch block, you’ll encounter the following error:WorkflowAbort
error is thrown by the Workflow SDK when a step executes successfully. When you run a step inside a try/catch block, you have two primary options:
-
Re-throw WorkflowAbort: If you need a try/catch block, re-throw the
WorkflowAbort
error: -
Avoid Running Steps in try/catch: The simplest solution is to not wrap steps in try/catch blocks. If you have a
context.run
inside try/catch, you can try putting the try/catch inside the context.run.
context.requestPayload
Becoming Undefined
Headers Considerations
In some frameworks, you may need to pass specific headers for the workflow to accessrequestPayload
:
- Try passing
Content-type: text/plain
orContent-type: application/json
headers when starting the workflow. - Note that
publishJSON
can only publishContent-type: application/json
.
context.call
Execution
During a workflow run, the endpoint will be called multiple times. While executing context.call
, the endpoint is called at least twice, with the SDK attempting to run the route function until the first step for custom authorization.
Accessing context.requestPayload
before any step can result in it becoming undefined:
context.call
, and resolving this specific interaction is on our roadmap. Note that if you are passing context.requestPayload
as a parameter to context.call
, the payload remains intact. However, during the actual execution of context.call
, the context.requestPayload
becomes undefined due to the SDK’s internal workflow processing steps.
To fix this issue, you can try adding a step which returns the payload:
Verification Failed Scenarios
When QStash signature verification is enabled, you might encounter an error like:- Ensure you start the workflow using
client.trigger
or by publishing to QStash. - Verify that
QSTASH_CURRENT_SIGNING_KEY
andQSTASH_NEXT_SIGNING_KEY
environment variables are correct. - Pass appropriate
Content-type
headers when starting the workflow.
Authorization Error Handling
Consider this workflow:- HTTP status: 400
- Error message:
Failed to authenticate Workflow request.
someCondition()
is non-deterministic, the recommended approach is to transform this condition into an explicit workflow step. Here’s the recommended pattern for handling such non-deterministic conditions:
Retry Configuration
Retry settings can be configured in three locations:-
Workflow Start (publish/publishJSON or client.trigger)
- Default retries: 3
-
Context Call
- Default retries: 0
-
Serve Options
- Default retries: 3
- Covers all other requests except the above two
Verbose Mode Diagnostics
This feature is not yet available in
workflow-py. See our
Roadmap for feature parity plans and
Changelog for updates.
Localhost in URL Warning
Deduplication Log
Since QStash has at least once delivery guarantee, there is a very small chance that a step will run twice. This is why we suggest idempotancy. When this happens, the duplicate step should print the following log and terminate:Network Issue Warnings
In rare instances, the workflow endpoint may complete its task successfully but fail to send a proper response. The SDK is designed to detect such occurrences. When this happens, the workflow endpoint will return a200
status code and log one of the following three warnings:
HTTPS Protocol Issue
The Workflow SDK automatically infers the protocol (HTTP or HTTPS) based on the incoming request object in your application. However, if your app is deployed behind a proxy (e.g., Railway or similar platforms), the proxy may terminate SSL and forward the request to your app using plain HTTP. This can cause the SDK to mistakenly infer that the request is using HTTP instead of HTTPS. To fix this, explicitly set theUPSTASH_WORKFLOW_URL
environment variable to the base URL of your application.
This disables automatic protocol inference and uses the provided static URL instead.
If you’re using Express.js behind a front-facing proxy, an alternative solution is to enable proxy trust so Express correctly identifies the original protocol from the X-Forwarded-Proto header: