Skip to content

feat(cloudflare): Only capture workflow step error on final retry attempt#21025

Open
JPeer264 wants to merge 2 commits into
developfrom
jp/cloudflare-workflow-single-error
Open

feat(cloudflare): Only capture workflow step error on final retry attempt#21025
JPeer264 wants to merge 2 commits into
developfrom
jp/cloudflare-workflow-single-error

Conversation

@JPeer264
Copy link
Copy Markdown
Member

closes #17421
closes JS-869

Cloudflare Workflows (wrangler 4.86.0+) now pass step context with attempt and config.retries.limit to step callbacks. When available, we use this to only capture errors on the final retry attempt, avoiding duplicate error events during retries.

For older wrangler versions without step context, the SDK falls back to legacy behavior (capturing errors on every attempt).

  • Add integration tests for step context behavior
    • This is specifically for the new behavior
  • Add e2e test for legacy behavior (wrangler 4.70.0)
    • This makes sure we won't break older wrangler versions
    • This test breaks when wrangler is updated, as with a newer wrangler version you get a higher compat version, therefore you get only 1 error instead of 3
    • A E2E test was chosen as it seem the compatibility_date has no effect with wrangler, so I couldn't make an integration test

@JPeer264 JPeer264 requested a review from timfish May 20, 2026 05:55
@JPeer264 JPeer264 self-assigned this May 20, 2026
@JPeer264 JPeer264 requested a review from a team as a code owner May 20, 2026 05:55
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 20, 2026

JS-869


// Only capture error on final attempt (attempt > retryLimit means no more retries left)
// or when step context is unavailable (legacy behavior - capture all errors)
const isFinalAttempt = !hasStepContext || attempt > retryLimit;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

note: On older versions this is not really the "final attempt", but treated as such, as there wouldn't be a good way of detecting it.

Comment thread dev-packages/cloudflare-integration-tests/suites/workflows/step-context/index.ts Dismissed
Comment thread dev-packages/cloudflare-integration-tests/suites/workflows/step-context/index.ts Dismissed
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b3a7d57. Configure here.

@JPeer264 JPeer264 force-pushed the jp/cloudflare-workflow-single-error branch from b3a7d57 to 34f09ad Compare May 20, 2026 06:05
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

size-limit report 📦

Path Size % Change Change
@sentry/browser 27.31 kB - -
@sentry/browser - with treeshaking flags 25.73 kB - -
@sentry/browser (incl. Tracing) 45.31 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 47.54 kB - -
@sentry/browser (incl. Tracing, Profiling) 50.29 kB - -
@sentry/browser (incl. Tracing, Replay) 84.91 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 74.42 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 89.63 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 102.23 kB - -
@sentry/browser (incl. Feedback) 44.5 kB - -
@sentry/browser (incl. sendFeedback) 32.12 kB - -
@sentry/browser (incl. FeedbackAsync) 37.24 kB - -
@sentry/browser (incl. Metrics) 28.39 kB - -
@sentry/browser (incl. Logs) 28.62 kB - -
@sentry/browser (incl. Metrics & Logs) 29.32 kB - -
@sentry/react 29.04 kB - -
@sentry/react (incl. Tracing) 47.54 kB - -
@sentry/vue 32.23 kB - -
@sentry/vue (incl. Tracing) 47.16 kB - -
@sentry/svelte 27.34 kB - -
CDN Bundle 29.71 kB - -
CDN Bundle (incl. Tracing) 47.83 kB - -
CDN Bundle (incl. Logs, Metrics) 31.2 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 49.08 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 70.52 kB - -
CDN Bundle (incl. Tracing, Replay) 85.33 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.49 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 91.2 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.36 kB - -
CDN Bundle - uncompressed 87.78 kB - -
CDN Bundle (incl. Tracing) - uncompressed 144.27 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 92.27 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 148.03 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 217 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 263.05 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 266.79 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 276.75 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 280.48 kB - -
@sentry/nextjs (client) 50.02 kB - -
@sentry/sveltekit (client) 45.79 kB - -
@sentry/core/server 76.31 kB - -
@sentry/core/browser 63.07 kB - -
@sentry/node-core 62.58 kB -0.01% -2 B 🔽
@sentry/node 164.33 kB - -
@sentry/node - without tracing 75.01 kB - -
@sentry/aws-serverless 87.23 kB -0.01% -1 B 🔽
@sentry/cloudflare (withSentry) - minified 173.12 kB +0.09% +145 B 🔺
@sentry/cloudflare (withSentry) 432.56 kB +0.1% +405 B 🔺

View base workflow run

Copy link
Copy Markdown
Collaborator

@timfish timfish left a comment

Choose a reason for hiding this comment

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

There's no way to catch every exception? Might we want only capturing the last failure to be configurable?

@JPeer264
Copy link
Copy Markdown
Member Author

JPeer264 commented May 20, 2026

There's no way to catch every exception? Might we want only capturing the last failure to be configurable?

Manual exceptions would still be send, only the step exception is not send. Not sure if there is a use case where every exception would make sense, as it is really just noise (after all, it was successful)

But having it configurable would be nice, in case there is a use case I can't think of. I just wonder how this could look like as it would work only with a specific wrangler version and would noop in older versions (might be misleading I guess). Do you have something already in mind?

@timfish
Copy link
Copy Markdown
Collaborator

timfish commented May 20, 2026

Do you have something already in mind?

I can't think of a nice way to add this as an option. I was just thinking that if a step errors for different reasons you'll only get the last one so some context might be lost. I guess most failures will be due to fetch failures or repetitive events anyway.

@JPeer264
Copy link
Copy Markdown
Member Author

I was just thinking that if a step errors for different reasons you'll only get the last one so some context might be lost.

Ah, no. That won't be the case. The following test actually checks if the actual errors are still getting send, but the actual step failure is only the last. With that, the user still gets 3 actual issues on these 3 attempts, but only 1 issue that the step failed:

.expectN(3, (envelope: Envelope): void => {
const [, items] = envelope;
const [itemHeader, itemBody] = items[0] as [{ type: string }, Record<string, unknown>];
expect(itemHeader.type).toBe('event');
expect(itemBody.exception).toBeDefined();
const exception = itemBody.exception as { values?: Array<{ value?: string }> };
expect(exception?.values?.[0]?.value).toMatch(/^Manual capture on attempt \d+$/);
})
.expect((envelope: Envelope): void => {
const [, items] = envelope;
const [itemHeader, itemBody] = items[0] as [{ type: string }, Record<string, unknown>];
expect(itemHeader.type).toBe('event');
expect(itemBody.exception).toBeDefined();
const exception = itemBody.exception as { values?: Array<{ value?: string }> };
expect(exception?.values?.[0]?.value).toBe('Intentional failure for retry test');
})

JPeer264 and others added 2 commits May 21, 2026 11:17
…empt

Cloudflare Workflows (wrangler 4.86.0+) now pass step context with
`attempt` and `config.retries.limit` to step callbacks. When available,
we use this to only capture errors on the final retry attempt, avoiding
duplicate error events during retries.

For older wrangler versions without step context, the SDK falls back to
legacy behavior (capturing errors on every attempt).

- Add step context detection in `instrumentedCallback`
- Add integration tests for step context behavior
- Add e2e test for legacy behavior (wrangler 4.70.0)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@JPeer264 JPeer264 force-pushed the jp/cloudflare-workflow-single-error branch from 34f09ad to ea2b9b2 Compare May 21, 2026 09:17
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.

[Cloudflare workflow] option to have only one error on step failure

3 participants