Skip to content

feat(core): bounded queue-redelivery retry on decryption failure during replay#2166

Open
TooTallNate wants to merge 1 commit into
mainfrom
nate/requeue-on-decryption-failure
Open

feat(core): bounded queue-redelivery retry on decryption failure during replay#2166
TooTallNate wants to merge 1 commit into
mainfrom
nate/requeue-on-decryption-failure

Conversation

@TooTallNate
Copy link
Copy Markdown
Member

Summary

Follow-up to #2145 (the RuntimeDecryptionError attribution fix), implementing the bounded-redelivery behavior @pranaygp described in this comment.

An AES-GCM authentication failure is terminal for the bytes/key of the current attempt — we must never continue executing the workflow on top of data we couldn't decrypt. But the failure is often not terminal for the run: when the ciphertext came from a transiently truncated or corrupted read of remotely-persisted data (a partial /refs response, an edge-cache miss returning a partial 200, a proxy drop during streaming), a fresh queue delivery re-fetches the event log and ref payloads from scratch and can succeed.

Previously we committed run_failed immediately, turning a potentially recoverable read failure into a terminal workflow failure.

What changed

Mirrors the existing replay-timeout bounded-redelivery precedent (handleReplayBudgetExhausted):

  • Managed worlds (processExitTriggersQueueRedelivery === true, e.g. world-vercel): on attempts ≤ DECRYPTION_FAILURE_MAX_RETRIES (3), the run handler exits the process — which the platform turns into a queue redelivery — so replay restarts from freshly-fetched persisted data. Once the retry budget is exhausted, it commits run_failed with RUNTIME_ERROR.
  • In-process worlds (world-local, dev servers, custom in-process worlds): no queue to re-fetch from, and process.exit() would kill the user's host, so the run fails immediately with RUNTIME_ERROR.

New shouldRedriveOnDecryptionFailure() helper (runtime/decryption-failure.ts) is pure (logging only) and returns whether the caller should redrive, keeping the process.exit / run_failed decision at the single existing call site in the run handler.

Test coverage

  • runtime/decryption-failure.test.ts — unit tests for the helper across managed/in-process worlds and the retry-budget boundary.
  • runtime.test.ts — end-to-end tests driving a real key mismatch (input encrypted with key A, run-key resolves to key B → auth-tag failure during input hydration):
    • early attempt on a managed world → process.exit(1), no run_failed
    • past the retry budget on a managed world → run_failed with RUNTIME_ERROR
    • in-process world → run_failed immediately, no exit

All @workflow/core tests pass (1073), full repo typecheck passes (40/40).

Notes

The longer-term improvement @pranaygp mentioned — detecting response truncation / integrity failure at the /refs transport boundary to classify the retryable case more directly — is out of scope here and would be a separate change on the world / workflow-server side.

…ng replay

Follow-up to #2145. An AES-GCM auth failure (RuntimeDecryptionError) is
terminal for the current attempt's bytes, but often transient at the run
level: when the ciphertext came from a truncated/corrupted read of
remotely-persisted data (e.g. a partial /refs response), a fresh queue
delivery re-fetches the event log + ref payloads and can succeed.

Mirror the replay-timeout bounded-redelivery precedent: on managed worlds
(processExitTriggersQueueRedelivery), exit the process to trigger queue
redelivery for up to DECRYPTION_FAILURE_MAX_RETRIES attempts, then commit
run_failed as RUNTIME_ERROR. In-process worlds fail immediately (no queue
to re-fetch from, and exiting would kill the host).
Copilot AI review requested due to automatic review settings May 29, 2026 19:24
@TooTallNate TooTallNate requested a review from a team as a code owner May 29, 2026 19:24
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 29, 2026

🦋 Changeset detected

Latest commit: fe5d9ca

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
@workflow/core Patch
@workflow/builders Patch
@workflow/cli Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/vitest Patch
@workflow/web-shared Patch
@workflow/web Patch
workflow Patch
@workflow/world-testing Patch
@workflow/astro Patch
@workflow/nest Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite Patch
@workflow/nuxt Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment May 29, 2026 7:28pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment May 29, 2026 7:28pm
example-workflow Ready Ready Preview, Comment May 29, 2026 7:28pm
workbench-astro-workflow Ready Ready Preview, Comment May 29, 2026 7:28pm
workbench-express-workflow Ready Ready Preview, Comment May 29, 2026 7:28pm
workbench-fastify-workflow Ready Ready Preview, Comment May 29, 2026 7:28pm
workbench-hono-workflow Ready Ready Preview, Comment May 29, 2026 7:28pm
workbench-nitro-workflow Ready Ready Preview, Comment May 29, 2026 7:28pm
workbench-nuxt-workflow Ready Ready Preview, Comment May 29, 2026 7:28pm
workbench-sveltekit-workflow Ready Ready Preview, Comment May 29, 2026 7:28pm
workbench-tanstack-start-workflow Ready Ready Preview, Comment May 29, 2026 7:28pm
workbench-vite-workflow Ready Ready Preview, Comment May 29, 2026 7:28pm
workflow-docs Ready Ready Preview, Comment, Open in v0 May 29, 2026 7:28pm
workflow-swc-playground Ready Ready Preview, Comment May 29, 2026 7:28pm
workflow-tarballs Ready Ready Preview, Comment May 29, 2026 7:28pm
workflow-web Ready Ready Preview, Comment May 29, 2026 7:28pm

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

📊 Benchmark Results

📈 Comparing against baseline from main branch. Green 🟢 = faster, Red 🔺 = slower.

workflow with no steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 0.044s (-1.4%) 1.006s (~) 0.962s 10 1.00x
💻 Local Nitro 0.047s (+8.8% 🔺) 1.011s (+0.5%) 0.964s 10 1.07x
💻 Local Next.js (Turbopack) 0.059s 1.006s 0.947s 10 1.35x
🐘 Postgres Next.js (Turbopack) 0.060s 1.011s 0.951s 10 1.37x
🐘 Postgres Express 0.060s (+3.6%) 1.014s (~) 0.954s 10 1.38x
🐘 Postgres Nitro 0.062s (-34.6% 🟢) 1.013s (-2.9%) 0.951s 10 1.43x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 0.307s (+21.9% 🔺) 2.328s (~) 2.021s 10 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.093s (-3.4%) 2.006s (~) 0.913s 10 1.00x
💻 Local Express 1.103s (-2.0%) 2.006s (~) 0.904s 10 1.01x
🐘 Postgres Express 1.108s (-3.3%) 2.010s (~) 0.902s 10 1.01x
🐘 Postgres Nitro 1.109s (-2.7%) 2.010s (~) 0.901s 10 1.01x
🐘 Postgres Next.js (Turbopack) 1.123s 2.010s 0.887s 10 1.03x
💻 Local Next.js (Turbopack) 1.126s 2.006s 0.880s 10 1.03x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.699s (-16.5% 🟢) 3.405s (-11.1% 🟢) 1.706s 10 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 10.541s (-3.0%) 11.020s (~) 0.479s 3 1.00x
💻 Local Nitro 10.554s (-3.6%) 11.022s (~) 0.469s 3 1.00x
💻 Local Express 10.555s (-3.4%) 11.023s (~) 0.468s 3 1.00x
🐘 Postgres Express 10.564s (-3.6%) 11.020s (~) 0.456s 3 1.00x
🐘 Postgres Next.js (Turbopack) 10.723s 11.017s 0.295s 3 1.02x
💻 Local Next.js (Turbopack) 10.778s 11.022s 0.244s 3 1.02x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 15.245s (-12.0% 🟢) 16.676s (-14.0% 🟢) 1.431s 2 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 13.747s (-5.8% 🟢) 14.021s (-6.7% 🟢) 0.274s 5 1.00x
💻 Local Nitro 13.757s (-8.7% 🟢) 14.027s (-12.5% 🟢) 0.270s 5 1.00x
💻 Local Express 13.828s (-7.6% 🟢) 14.028s (-6.7% 🟢) 0.200s 5 1.01x
🐘 Postgres Express 13.858s (-5.0%) 14.020s (-6.7% 🟢) 0.162s 5 1.01x
🐘 Postgres Next.js (Turbopack) 14.005s 14.416s 0.412s 5 1.02x
💻 Local Next.js (Turbopack) 14.324s 15.030s 0.706s 4 1.04x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 23.557s (-55.2% 🟢) 24.960s (-54.3% 🟢) 1.402s 3 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 12.498s (-25.5% 🟢) 13.024s (-23.5% 🟢) 0.526s 7 1.00x
🐘 Postgres Nitro 12.596s (-9.8% 🟢) 13.020s (-9.0% 🟢) 0.423s 7 1.01x
💻 Local Express 12.616s (-24.0% 🟢) 13.025s (-23.5% 🟢) 0.409s 7 1.01x
🐘 Postgres Express 12.627s (-9.9% 🟢) 13.020s (-10.8% 🟢) 0.394s 7 1.01x
🐘 Postgres Next.js (Turbopack) 12.985s 13.302s 0.318s 7 1.04x
💻 Local Next.js (Turbopack) 13.526s 14.027s 0.501s 7 1.08x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 33.034s (-91.6% 🟢) 35.227s (-91.1% 🟢) 2.193s 3 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.175s (-7.8% 🟢) 2.008s (~) 0.833s 15 1.00x
🐘 Postgres Express 1.194s (-5.3% 🟢) 2.009s (~) 0.814s 15 1.02x
🐘 Postgres Next.js (Turbopack) 1.197s 2.008s 0.811s 15 1.02x
💻 Local Express 1.223s (-17.9% 🟢) 2.006s (~) 0.784s 15 1.04x
💻 Local Nitro 1.231s (-24.5% 🟢) 2.006s (-3.3%) 0.775s 15 1.05x
💻 Local Next.js (Turbopack) 1.324s 2.006s 0.682s 15 1.13x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.196s (-5.9% 🟢) 4.953s (~) 1.757s 7 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.261s (-46.4% 🟢) 2.007s (-33.3% 🟢) 0.746s 15 1.00x
🐘 Postgres Express 1.281s (-45.7% 🟢) 2.007s (-33.3% 🟢) 0.726s 15 1.02x
🐘 Postgres Next.js (Turbopack) 1.315s 2.007s 0.692s 15 1.04x
💻 Local Nitro 1.868s (-40.6% 🟢) 2.150s (-44.7% 🟢) 0.282s 14 1.48x
💻 Local Next.js (Turbopack) 1.886s 2.150s 0.264s 14 1.50x
💻 Local Express 1.976s (-33.1% 🟢) 2.469s (-28.5% 🟢) 0.493s 13 1.57x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 4.698s (-33.8% 🟢) 6.528s (-26.7% 🟢) 1.830s 5 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.403s (-59.8% 🟢) 2.008s (-49.9% 🟢) 0.605s 15 1.00x
🐘 Postgres Nitro 1.410s (-59.5% 🟢) 2.008s (-49.9% 🟢) 0.598s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.544s 2.009s 0.465s 15 1.10x
💻 Local Nitro 5.304s (-36.5% 🟢) 5.847s (-35.2% 🟢) 0.542s 6 3.78x
💻 Local Express 5.699s (-31.7% 🟢) 6.182s (-31.5% 🟢) 0.483s 6 4.06x
💻 Local Next.js (Turbopack) 6.048s 6.416s 0.369s 5 4.31x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 5.863s (-34.2% 🟢) 8.111s (-26.0% 🟢) 2.248s 4 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.176s (-6.4% 🟢) 2.007s (~) 0.831s 15 1.00x
🐘 Postgres Express 1.178s (-6.3% 🟢) 2.009s (~) 0.830s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.204s 2.008s 0.803s 15 1.02x
💻 Local Next.js (Turbopack) 1.356s 2.007s 0.650s 15 1.15x
💻 Local Nitro 1.526s (-18.2% 🟢) 2.006s (-14.3% 🟢) 0.480s 15 1.30x
💻 Local Express 1.594s (-15.8% 🟢) 2.007s (-15.1% 🟢) 0.412s 15 1.36x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.992s (+2.1%) 4.548s (-2.0%) 1.556s 7 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.265s (-45.9% 🟢) 2.010s (-33.2% 🟢) 0.745s 15 1.00x
🐘 Postgres Express 1.298s (-44.6% 🟢) 2.008s (-33.3% 🟢) 0.711s 15 1.03x
🐘 Postgres Next.js (Turbopack) 1.312s 2.007s 0.695s 15 1.04x
💻 Local Next.js (Turbopack) 2.032s 2.507s 0.475s 12 1.61x
💻 Local Nitro 2.122s (-30.8% 🟢) 2.736s (-29.6% 🟢) 0.614s 11 1.68x
💻 Local Express 2.223s (-29.0% 🟢) 2.736s (-27.3% 🟢) 0.512s 11 1.76x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 4.051s (+28.9% 🔺) 6.053s (+33.9% 🔺) 2.002s 5 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.384s (-60.2% 🟢) 2.008s (-49.9% 🟢) 0.625s 15 1.00x
🐘 Postgres Express 1.456s (-58.4% 🟢) 2.008s (-49.9% 🟢) 0.553s 15 1.05x
🐘 Postgres Next.js (Turbopack) 1.517s 2.008s 0.491s 15 1.10x
💻 Local Next.js (Turbopack) 6.078s 6.616s 0.538s 5 4.39x
💻 Local Express 6.084s (-30.9% 🟢) 6.417s (-30.8% 🟢) 0.333s 5 4.40x
💻 Local Nitro 6.391s (-30.1% 🟢) 6.814s (-32.0% 🟢) 0.423s 5 4.62x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 5.600s (-17.1% 🟢) 7.584s (-11.2% 🟢) 1.984s 4 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.574s (-30.0% 🟢) 1.024s (+1.7%) 0.449s 59 1.00x
🐘 Postgres Express 0.605s (-27.9% 🟢) 1.025s (~) 0.419s 59 1.05x
💻 Local Express 0.645s (-34.4% 🟢) 1.040s (-3.4%) 0.394s 58 1.12x
🐘 Postgres Next.js (Turbopack) 0.681s 1.059s 0.378s 57 1.19x
💻 Local Nitro 0.695s (-29.1% 🟢) 1.093s (~) 0.397s 57 1.21x
💻 Local Next.js (Turbopack) 0.891s 1.039s 0.148s 58 1.55x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 5.754s (-60.3% 🟢) 7.407s (-53.9% 🟢) 1.653s 9 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.376s (-28.6% 🟢) 2.030s (-3.3%) 0.654s 45 1.00x
🐘 Postgres Express 1.428s (-27.8% 🟢) 2.030s (-10.1% 🟢) 0.602s 45 1.04x
💻 Local Nitro 1.516s (-50.0% 🟢) 2.006s (-46.6% 🟢) 0.490s 45 1.10x
🐘 Postgres Next.js (Turbopack) 1.559s 2.008s 0.449s 45 1.13x
💻 Local Express 1.575s (-47.8% 🟢) 2.007s (-44.0% 🟢) 0.432s 45 1.14x
💻 Local Next.js (Turbopack) 2.104s 3.008s 0.904s 30 1.53x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 15.862s (-68.1% 🟢) 17.650s (-65.9% 🟢) 1.788s 6 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.699s (-34.2% 🟢) 3.083s (-33.0% 🟢) 0.384s 40 1.00x
🐘 Postgres Express 2.770s (-30.6% 🟢) 3.163s (-27.6% 🟢) 0.393s 39 1.03x
🐘 Postgres Next.js (Turbopack) 3.050s 3.706s 0.656s 33 1.13x
💻 Local Nitro 3.417s (-63.3% 🟢) 4.181s (-58.3% 🟢) 0.764s 29 1.27x
💻 Local Express 3.441s (-62.6% 🟢) 4.043s (-59.6% 🟢) 0.602s 30 1.27x
💻 Local Next.js (Turbopack) 4.257s 5.010s 0.753s 24 1.58x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 31.460s (-70.6% 🟢) 34.170s (-68.6% 🟢) 2.711s 4 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.219s (-22.8% 🟢) 1.006s (~) 0.787s 60 1.00x
🐘 Postgres Express 0.228s (-19.2% 🟢) 1.007s (~) 0.779s 60 1.04x
🐘 Postgres Next.js (Turbopack) 0.236s 1.006s 0.770s 60 1.08x
💻 Local Nitro 0.447s (-26.0% 🟢) 1.005s (-1.7%) 0.557s 60 2.05x
💻 Local Express 0.474s (-15.4% 🟢) 1.005s (~) 0.531s 60 2.17x
💻 Local Next.js (Turbopack) 0.580s 1.039s 0.459s 58 2.65x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.428s (+20.1% 🔺) 3.947s (+4.0%) 1.519s 16 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.361s (-27.4% 🟢) 1.007s (~) 0.646s 90 1.00x
🐘 Postgres Express 0.364s (-28.7% 🟢) 1.007s (~) 0.643s 90 1.01x
🐘 Postgres Next.js (Turbopack) 0.404s 1.006s 0.602s 90 1.12x
💻 Local Nitro 2.150s (-15.3% 🟢) 2.656s (-11.8% 🟢) 0.506s 34 5.96x
💻 Local Express 2.176s (-13.4% 🟢) 2.823s (-6.2% 🟢) 0.647s 32 6.03x
💻 Local Next.js (Turbopack) 2.482s 3.149s 0.667s 29 6.88x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 5.991s (+69.5% 🔺) 7.449s (+43.4% 🔺) 1.458s 13 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 0.685s (-16.4% 🟢) 1.007s (-1.1%) 0.322s 120 1.00x
🐘 Postgres Nitro 0.699s (-11.5% 🟢) 1.006s (~) 0.307s 120 1.02x
🐘 Postgres Next.js (Turbopack) 0.793s 1.006s 0.213s 120 1.16x
💻 Local Nitro 9.912s (-11.4% 🟢) 10.361s (-11.2% 🟢) 0.449s 12 14.48x
💻 Local Express 10.369s (-7.3% 🟢) 10.941s (-8.4% 🟢) 0.572s 11 15.15x
💻 Local Next.js (Turbopack) 11.167s 11.760s 0.593s 11 16.31x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 18.937s (+83.4% 🔺) 20.678s (+68.3% 🔺) 1.741s 6 1.00x
▲ Vercel Express ⚠️ missing - - - -
▲ Vercel Nitro ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack)

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.167s (+446.0% 🔺) 2.005s (+99.6% 🔺) 0.013s (+2.4%) 2.020s (+98.3% 🔺) 0.853s 10 1.00x
💻 Local Express 1.167s (+486.4% 🔺) 2.005s (+99.6% 🔺) 0.012s (+1.7%) 2.020s (+98.4% 🔺) 0.853s 10 1.00x
🐘 Postgres Express 1.168s (+469.3% 🔺) 2.000s (+100.3% 🔺) 0.001s (-31.3% 🟢) 2.011s (+98.8% 🔺) 0.843s 10 1.00x
🐘 Postgres Nitro 1.171s (+471.4% 🔺) 1.998s (+99.9% 🔺) 0.001s (-26.7% 🟢) 2.010s (+98.8% 🔺) 0.839s 10 1.00x
🐘 Postgres Next.js (Turbopack) 1.184s 2.001s 0.001s 2.009s 0.825s 10 1.01x
💻 Local Next.js (Turbopack) 1.204s 2.004s 0.012s 2.020s 0.816s 10 1.03x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.799s (-59.2% 🟢) 4.021s (-53.5% 🟢) 1.803s (+185.3% 🔺) 6.288s (-35.8% 🟢) 3.490s 10 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.600s (+156.4% 🔺) 2.006s (+99.2% 🔺) 0.004s (-4.1%) 2.027s (+98.3% 🔺) 0.427s 30 1.00x
🐘 Postgres Express 1.622s (+157.5% 🔺) 2.006s (+99.3% 🔺) 0.004s (+4.4%) 2.027s (+98.2% 🔺) 0.405s 30 1.01x
🐘 Postgres Next.js (Turbopack) 1.628s 2.008s 0.003s 2.023s 0.396s 30 1.02x
💻 Local Next.js (Turbopack) 1.805s 2.009s 0.012s 2.024s 0.220s 30 1.13x
💻 Local Nitro 1.992s (+137.6% 🔺) 2.010s (+98.6% 🔺) 0.010s (+10.1% 🔺) 2.423s (+117.1% 🔺) 0.430s 25 1.25x
💻 Local Express 2.000s (+164.1% 🔺) 2.010s (+95.3% 🔺) 0.011s (+14.1% 🔺) 2.394s (+130.2% 🔺) 0.394s 27 1.25x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 6.228s (-63.2% 🟢) 7.662s (-58.0% 🟢) 0.204s (-3.4%) 8.377s (-55.8% 🟢) 2.149s 8 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.725s (-25.2% 🟢) 1.033s (-17.2% 🟢) 0.000s (+26.3% 🔺) 1.061s (-15.6% 🟢) 0.336s 57 1.00x
🐘 Postgres Express 0.740s (-23.0% 🟢) 1.069s (-16.4% 🟢) 0.000s (-17.9% 🟢) 1.080s (-17.3% 🟢) 0.340s 56 1.02x
🐘 Postgres Next.js (Turbopack) 0.766s 1.053s 0.000s 1.060s 0.293s 57 1.06x
💻 Local Nitro 1.377s (+12.6% 🔺) 2.013s (~) 0.000s (+100.0% 🔺) 2.015s (~) 0.638s 30 1.90x
💻 Local Express 1.434s (+17.1% 🔺) 2.014s (~) 0.001s (+140.0% 🔺) 2.017s (~) 0.582s 30 1.98x
💻 Local Next.js (Turbopack) 1.550s 2.014s 0.000s 2.018s 0.467s 30 2.14x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 4.514s (-55.7% 🟢) 5.902s (-48.8% 🟢) 0.001s (+Infinity% 🔺) 6.330s (-47.5% 🟢) 1.816s 11 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.384s (-22.7% 🟢) 2.101s (-1.9%) 0.000s (-3.4%) 2.117s (-2.6%) 0.733s 29 1.00x
🐘 Postgres Express 1.433s (-19.1% 🟢) 2.066s (-5.1% 🟢) 0.000s (+Infinity% 🔺) 2.084s (-5.2% 🟢) 0.651s 29 1.04x
🐘 Postgres Next.js (Turbopack) 1.597s 2.146s 0.000s 2.153s 0.556s 28 1.15x
💻 Local Next.js (Turbopack) 3.034s 3.617s 0.001s 3.620s 0.587s 17 2.19x
💻 Local Nitro 3.129s (-7.6% 🟢) 3.840s (-4.8%) 0.001s (+28.9% 🔺) 3.843s (-4.8%) 0.714s 16 2.26x
💻 Local Express 3.191s (-8.0% 🟢) 3.779s (-6.3% 🟢) 0.001s (-14.1% 🟢) 3.781s (-6.3% 🟢) 0.591s 16 2.31x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 5.741s (+2.2%) 7.212s (+3.3%) 0.000s (~) 7.630s (+1.2%) 1.889s 8 1.00x
▲ Vercel Express ⚠️ missing - - - - -
▲ Vercel Nitro ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Nitro 13/21
🐘 Postgres Nitro 16/21
▲ Vercel Next.js (Turbopack) 21/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 15/21
Next.js (Turbopack) 🐘 Postgres 20/21
Nitro 🐘 Postgres 17/21
Column Definitions
  • Workflow Time: Runtime reported by workflow (completedAt - createdAt) - primary metric
  • TTFB: Time to First Byte - time from workflow start until first stream byte received (stream benchmarks only)
  • Slurp: Time from first byte to complete stream consumption (stream benchmarks only)
  • Wall Time: Total testbench time (trigger workflow + poll for result)
  • Overhead: Testbench overhead (Wall Time - Workflow Time)
  • Samples: Number of benchmark iterations run
  • vs Fastest: How much slower compared to the fastest configuration for this benchmark

Worlds:

  • 💻 Local: In-memory filesystem world (local development)
  • 🐘 Postgres: PostgreSQL database world (local development)
  • ▲ Vercel: Vercel production/preview deployment
  • 🌐 Turso: Community world (local development)
  • 🌐 MongoDB: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Jazz: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: success
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
❌ ▲ Vercel Production 1262 4 219 1485
✅ 💻 Local Development 1671 0 219 1890
❌ 📦 Local Production 1670 1 219 1890
✅ 🐘 Local Postgres 1671 0 219 1890
✅ 📋 Other 769 0 176 945
Total 7043 5 1052 8100

❌ Failed Tests

▲ Vercel Production (4 failed)

astro (1 failed):

  • AbortController abortFromStepWorkflow: step abort cancels an in-flight sibling step

fastify (1 failed):

nextjs-turbopack (1 failed):

  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands

nextjs-webpack (1 failed):

📦 Local Production (1 failed)

express-stable (1 failed):

  • AbortController abortThrowIfAbortedWorkflow: throwIfAborted causes FatalError, no retries

Details by Category

❌ ▲ Vercel Production
App Passed Failed Skipped
❌ astro 108 1 26
✅ example 109 0 26
✅ express 109 0 26
❌ fastify 108 1 26
✅ hono 109 0 26
❌ nextjs-turbopack 132 1 2
❌ nextjs-webpack 132 1 2
✅ nitro 109 0 26
✅ nuxt 109 0 26
✅ sveltekit 128 0 7
✅ vite 109 0 26
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 110 0 25
✅ express-stable 110 0 25
✅ fastify-stable 110 0 25
✅ hono-stable 110 0 25
✅ nextjs-turbopack-canary 116 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 135 0 0
✅ nextjs-turbopack-stable-lazy-discovery-enabled 135 0 0
✅ nextjs-webpack-canary 116 0 19
✅ nextjs-webpack-stable-lazy-discovery-disabled 135 0 0
✅ nextjs-webpack-stable-lazy-discovery-enabled 135 0 0
✅ nitro-stable 110 0 25
✅ nuxt-stable 110 0 25
✅ sveltekit-stable 129 0 6
✅ vite-stable 110 0 25
❌ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 110 0 25
❌ express-stable 109 1 25
✅ fastify-stable 110 0 25
✅ hono-stable 110 0 25
✅ nextjs-turbopack-canary 116 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 135 0 0
✅ nextjs-turbopack-stable-lazy-discovery-enabled 135 0 0
✅ nextjs-webpack-canary 116 0 19
✅ nextjs-webpack-stable-lazy-discovery-disabled 135 0 0
✅ nextjs-webpack-stable-lazy-discovery-enabled 135 0 0
✅ nitro-stable 110 0 25
✅ nuxt-stable 110 0 25
✅ sveltekit-stable 129 0 6
✅ vite-stable 110 0 25
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 110 0 25
✅ express-stable 110 0 25
✅ fastify-stable 110 0 25
✅ hono-stable 110 0 25
✅ nextjs-turbopack-canary 116 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 135 0 0
✅ nextjs-turbopack-stable-lazy-discovery-enabled 135 0 0
✅ nextjs-webpack-canary 116 0 19
✅ nextjs-webpack-stable-lazy-discovery-disabled 135 0 0
✅ nextjs-webpack-stable-lazy-discovery-enabled 135 0 0
✅ nitro-stable 110 0 25
✅ nuxt-stable 110 0 25
✅ sveltekit-stable 129 0 6
✅ vite-stable 110 0 25
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 110 0 25
✅ e2e-local-dev-tanstack-start- 110 0 25
✅ e2e-local-postgres-nest-stable 110 0 25
✅ e2e-local-postgres-tanstack-start- 110 0 25
✅ e2e-local-prod-nest-stable 110 0 25
✅ e2e-local-prod-tanstack-start- 110 0 25
✅ e2e-vercel-prod-tanstack-start 109 0 26

📋 View full workflow run


Some E2E test jobs failed:

  • Vercel Prod: failure
  • Local Dev: success
  • Local Prod: failure
  • Local Postgres: success
  • Windows: failure

Check the workflow run for details.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 bounded queue-redelivery behavior for replay-time RuntimeDecryptionErrors in @workflow/core, allowing managed worlds to retry transient persisted-data read corruption before marking the run failed.

Changes:

  • Adds a decryption-failure retry decision helper and retry budget constant.
  • Integrates the helper into the workflow runtime error path before writing run_failed.
  • Adds unit and runtime coverage plus a patch changeset.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/core/src/runtime/decryption-failure.ts Adds redelivery decision logic and logging for replay decryption failures.
packages/core/src/runtime/decryption-failure.test.ts Covers managed/in-process world behavior and retry-budget boundaries.
packages/core/src/runtime/constants.ts Defines the decryption-failure retry budget.
packages/core/src/runtime.ts Redrives managed-world runs on eligible RuntimeDecryptionErrors before failing the run.
packages/core/src/runtime.test.ts Adds end-to-end key-mismatch tests for redelivery and failure behavior.
.changeset/requeue-on-decryption-failure.md Adds a patch changeset for @workflow/core.

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

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.

2 participants