Skip to content

Track Vercel request IDs on workflow events for observability#997

Draft
pranaygp wants to merge 3 commits intomainfrom
claude/add-request-id-tracking-LcNAm
Draft

Track Vercel request IDs on workflow events for observability#997
pranaygp wants to merge 3 commits intomainfrom
claude/add-request-id-tracking-LcNAm

Conversation

@pranaygp
Copy link
Collaborator

@pranaygp pranaygp commented Feb 10, 2026

Summary

This PR adds support for tracking Vercel request IDs (x-vercel-id header) throughout the workflow execution lifecycle. Request IDs are now captured from incoming HTTP requests and propagated to all workflow and step events, enabling better correlation between request logs and workflow executions in observability systems.

Backend PR needed for store these IDs: https://github.com/vercel/workflow-server/pull/251

Key Changes

  • Request ID Propagation: Added requestId parameter to the CreateEventParams interface and threaded it through all event creation calls in the workflow runtime, step handler, and suspension handler
  • Queue Handler Integration:
    • Modified @vercel/queue handler wrapper to extract the x-vercel-id header from incoming requests using AsyncLocalStorage
    • Updated local queue implementation to extract and pass the request ID to handlers
    • Added requestId to the queue handler metadata interface
  • Event Storage: Updated PostgreSQL and local file-based event storage implementations to persist the requestId field on events
  • API Integration: Modified Vercel API client to include requestId in event creation requests
  • Type Definitions: Added requestId field to EventSchema and documented it in relevant interfaces
  • Database Schema: Added requestId column to PostgreSQL events table

Implementation Details

  • Used Node.js AsyncLocalStorage to propagate request IDs through the async call chain in the Vercel queue handler, since the @vercel/queue library abstracts away the raw Request object
  • Request IDs are optional and gracefully handled when not available (e.g., in local development)
  • The request ID is passed through the entire workflow lifecycle: from initial run creation through step execution to completion/failure events
  • All event creation calls now include the request ID parameter, enabling comprehensive request tracing across the workflow system

https://claude.ai/code/session_01TqomNcQoixA1HzxvFFBsu2

Extract the x-vercel-id header from incoming requests in queue handlers
and propagate it through event creation so every workflow event records
which Vercel request created it. This enables linking Vercel request
logs with workflow runs and step executions in observability.

Changes:
- Add requestId to Queue handler metadata and CreateEventParams
- Add requestId field to Event schema
- Extract x-vercel-id in Vercel queue handler (via AsyncLocalStorage)
  and local queue handler
- Pass requestId through workflow runtime, step handler, and
  suspension handler to all event creation calls
- Store requestId in world-vercel, world-local, and world-postgres
  event storage
- Accept optional requestId in StartOptions for initial workflow start

https://claude.ai/code/session_01TqomNcQoixA1HzxvFFBsu2
Copilot AI review requested due to automatic review settings February 10, 2026 23:28
@changeset-bot
Copy link

changeset-bot bot commented Feb 10, 2026

🦋 Changeset detected

Latest commit: bd96bc3

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

This PR includes changesets to release 18 packages
Name Type
@workflow/world Patch
@workflow/world-vercel Patch
@workflow/world-local Patch
@workflow/world-postgres Patch
@workflow/core Patch
@workflow/web-shared Patch
@workflow/cli Patch
@workflow/world-testing Patch
@workflow/builders Patch
@workflow/next Patch
@workflow/nitro Patch
workflow 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
Contributor

vercel bot commented Feb 10, 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 Feb 11, 2026 0:05am
example-nextjs-workflow-webpack Ready Ready Preview, Comment Feb 11, 2026 0:05am
example-workflow Ready Ready Preview, Comment Feb 11, 2026 0:05am
workbench-astro-workflow Ready Ready Preview, Comment Feb 11, 2026 0:05am
workbench-express-workflow Ready Ready Preview, Comment Feb 11, 2026 0:05am
workbench-fastify-workflow Ready Ready Preview, Comment Feb 11, 2026 0:05am
workbench-hono-workflow Ready Ready Preview, Comment Feb 11, 2026 0:05am
workbench-nitro-workflow Ready Ready Preview, Comment Feb 11, 2026 0:05am
workbench-nuxt-workflow Ready Ready Preview, Comment Feb 11, 2026 0:05am
workbench-sveltekit-workflow Ready Ready Preview, Comment Feb 11, 2026 0:05am
workbench-vite-workflow Ready Ready Preview, Comment Feb 11, 2026 0:05am
workflow-docs Ready Ready Preview, Comment, Open in v0 Feb 11, 2026 0:05am
workflow-nest Ready Ready Preview, Comment Feb 11, 2026 0:05am
workflow-swc-playground Ready Ready Preview, Comment Feb 11, 2026 0:05am

@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 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 🥇 Nitro 0.032s (-6.2% 🟢) 1.005s (~) 0.973s 10 1.00x
💻 Local Express 0.032s (-1.2%) 1.005s (~) 0.973s 10 1.01x
💻 Local Next.js (Turbopack) 0.035s 1.005s 0.970s 10 1.08x
🌐 Redis Next.js (Turbopack) 0.045s 1.005s 0.960s 10 1.40x
🌐 MongoDB Next.js (Turbopack) 0.095s 1.008s 0.913s 10 2.98x
🐘 Postgres Express 0.177s (-14.6% 🟢) 1.010s (~) 0.833s 10 5.54x
🐘 Postgres Nitro 0.218s (-40.9% 🟢) 1.012s (~) 0.794s 10 6.82x
🐘 Postgres Next.js (Turbopack) 0.249s 1.010s 0.761s 10 7.81x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 0.903s (-16.0% 🟢) 2.550s (-9.6% 🟢) 1.647s 10 1.00x
▲ Vercel Next.js (Turbopack) 0.922s (-23.3% 🟢) 2.808s (-5.8% 🟢) 1.886s 10 1.02x
▲ Vercel Express 0.955s (+1.4%) 2.807s (+6.8% 🔺) 1.852s 10 1.06x

🔍 Observability: Nitro | Next.js (Turbopack) | Express

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 1.081s 2.006s 0.925s 10 1.00x
🌐 Redis Next.js (Turbopack) 1.103s 2.006s 0.903s 10 1.02x
💻 Local Nitro 1.103s (~) 2.005s (~) 0.902s 10 1.02x
💻 Local Express 1.107s (~) 2.005s (~) 0.897s 10 1.02x
🌐 MongoDB Next.js (Turbopack) 1.319s 2.009s 0.690s 10 1.22x
🐘 Postgres Next.js (Turbopack) 1.930s 2.012s 0.082s 10 1.78x
🐘 Postgres Express 2.244s (+4.9%) 3.013s (~) 0.769s 10 2.08x
🐘 Postgres Nitro 2.433s (+8.7% 🔺) 3.014s (~) 0.581s 10 2.25x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.869s (+1.4%) 4.226s (~) 1.356s 10 1.00x
▲ Vercel Express 2.948s (-1.7%) 4.534s (+2.8%) 1.586s 10 1.03x
▲ Vercel Next.js (Turbopack) 2.961s (-5.1% 🟢) 4.305s (-0.7%) 1.344s 10 1.03x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 10.582s 11.024s 0.442s 3 1.00x
🌐 Redis Next.js (Turbopack) 10.714s 11.021s 0.307s 3 1.01x
💻 Local Nitro 10.820s (~) 11.020s (~) 0.201s 3 1.02x
💻 Local Express 10.840s (~) 11.023s (~) 0.184s 3 1.02x
🌐 MongoDB Next.js (Turbopack) 12.309s 13.026s 0.717s 3 1.16x
🐘 Postgres Next.js (Turbopack) 15.325s 16.045s 0.720s 2 1.45x
🐘 Postgres Nitro 20.292s (+1.0%) 21.062s (~) 0.770s 2 1.92x
🐘 Postgres Express 20.404s (-0.5%) 21.055s (~) 0.650s 2 1.93x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 20.349s (+4.2%) 22.745s (+9.5% 🔺) 2.397s 2 1.00x
▲ Vercel Next.js (Turbopack) 20.628s (-2.4%) 22.722s (+2.4%) 2.094s 2 1.01x
▲ Vercel Nitro 21.159s (+1.2%) 23.765s (+8.8% 🔺) 2.607s 2 1.04x

🔍 Observability: Express | Next.js (Turbopack) | Nitro

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 26.834s 27.050s 0.216s 3 1.00x
💻 Local Next.js (Turbopack) 26.854s 27.051s 0.197s 3 1.00x
💻 Local Nitro 27.445s (~) 28.050s (~) 0.605s 3 1.02x
💻 Local Express 27.504s (~) 28.052s (~) 0.547s 3 1.02x
🌐 MongoDB Next.js (Turbopack) 30.540s 31.042s 0.502s 2 1.14x
🐘 Postgres Next.js (Turbopack) 37.925s 38.600s 0.675s 2 1.41x
🐘 Postgres Express 37.992s (-24.5% 🟢) 38.596s (-24.5% 🟢) 0.604s 2 1.42x
🐘 Postgres Nitro 50.309s (~) 50.627s (-1.0%) 0.318s 2 1.87x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 50.877s (+0.6%) 52.674s (+2.4%) 1.797s 2 1.00x
▲ Vercel Express 51.045s (~) 52.823s (-0.8%) 1.778s 2 1.00x
▲ Vercel Next.js (Turbopack) 53.156s (+4.2%) 54.688s (+3.3%) 1.532s 2 1.04x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 54.452s 55.101s 0.649s 2 1.00x
💻 Local Next.js (Turbopack) 55.695s 56.103s 0.408s 2 1.02x
💻 Local Nitro 57.064s (~) 57.100s (-1.7%) 0.036s 2 1.05x
💻 Local Express 57.292s (~) 58.102s (+1.8%) 0.810s 2 1.05x
🌐 MongoDB Next.js (Turbopack) 60.996s 61.064s 0.068s 2 1.12x
🐘 Postgres Express 75.670s (-23.6% 🟢) 76.185s (-23.2% 🟢) 0.515s 2 1.39x
🐘 Postgres Next.js (Turbopack) 90.153s 90.724s 0.572s 2 1.66x
🐘 Postgres Nitro 100.204s (~) 100.222s (-1.0%) 0.018s 1 1.84x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 109.835s (+4.9%) 111.233s (+5.1% 🔺) 1.398s 1 1.00x
▲ Vercel Nitro 110.549s (+6.5% 🔺) 111.252s (+5.8% 🔺) 0.703s 1 1.01x
▲ Vercel Next.js (Turbopack) 111.845s (+5.5% 🔺) 114.748s (+7.4% 🔺) 2.903s 1 1.02x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 1.233s 2.006s 0.773s 15 1.00x
💻 Local Next.js (Turbopack) 1.356s 2.005s 0.649s 15 1.10x
💻 Local Nitro 1.412s (~) 2.005s (~) 0.593s 15 1.15x
💻 Local Express 1.416s (-0.8%) 2.006s (~) 0.590s 15 1.15x
🐘 Postgres Express 1.919s (-11.1% 🟢) 2.078s (-31.0% 🟢) 0.159s 15 1.56x
🌐 MongoDB Next.js (Turbopack) 2.153s 3.009s 0.856s 10 1.75x
🐘 Postgres Next.js (Turbopack) 2.186s 2.742s 0.555s 11 1.77x
🐘 Postgres Nitro 2.237s (-8.7% 🟢) 3.013s (~) 0.776s 10 1.81x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.958s (-8.8% 🟢) 4.099s (-15.6% 🟢) 1.140s 8 1.00x
▲ Vercel Express 3.129s (+10.2% 🔺) 4.335s (+3.5%) 1.206s 7 1.06x
▲ Vercel Next.js (Turbopack) 3.274s (+3.6%) 4.418s (-1.9%) 1.144s 7 1.11x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 2.335s 3.007s 0.671s 10 1.00x
🌐 Redis Next.js (Turbopack) 2.500s 3.007s 0.507s 10 1.07x
💻 Local Nitro 2.513s (-1.6%) 3.007s (~) 0.494s 10 1.08x
💻 Local Express 2.614s (+2.3%) 3.008s (~) 0.393s 10 1.12x
🌐 MongoDB Next.js (Turbopack) 4.739s 5.179s 0.440s 6 2.03x
🐘 Postgres Nitro 8.514s (+17.9% 🔺) 9.042s (+12.5% 🔺) 0.527s 4 3.65x
🐘 Postgres Express 9.369s (+23.8% 🔺) 9.781s (+21.7% 🔺) 0.412s 4 4.01x
🐘 Postgres Next.js (Turbopack) 12.264s 13.037s 0.773s 3 5.25x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.608s (+15.5% 🔺) 4.845s (+13.6% 🔺) 1.237s 7 1.00x
▲ Vercel Nitro 3.772s (+18.6% 🔺) 5.011s (+7.7% 🔺) 1.239s 6 1.05x
▲ Vercel Next.js (Turbopack) 4.486s (+39.5% 🔺) 6.297s (+44.7% 🔺) 1.811s 5 1.24x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 4.029s 4.581s 0.553s 7 1.00x
💻 Local Next.js (Turbopack) 6.594s 6.817s 0.223s 5 1.64x
💻 Local Nitro 7.013s (-4.0%) 7.520s (-6.2% 🟢) 0.506s 4 1.74x
💻 Local Express 7.549s (+5.6% 🔺) 8.023s (~) 0.474s 4 1.87x
🌐 MongoDB Next.js (Turbopack) 9.974s 10.688s 0.714s 3 2.48x
🐘 Postgres Nitro 45.765s (-6.8% 🟢) 46.143s (-7.9% 🟢) 0.378s 1 11.36x
🐘 Postgres Express 49.356s (-0.9%) 50.123s (~) 0.767s 1 12.25x
🐘 Postgres Next.js (Turbopack) 54.991s 55.121s 0.130s 1 13.65x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 4.082s (+11.2% 🔺) 5.448s (+10.6% 🔺) 1.366s 6 1.00x
▲ Vercel Nitro 4.105s (+11.8% 🔺) 5.541s (-9.6% 🟢) 1.435s 6 1.01x
▲ Vercel Next.js (Turbopack) 4.341s (+14.1% 🔺) 5.732s (~) 1.391s 6 1.06x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 1.269s 2.006s 0.737s 15 1.00x
💻 Local Next.js (Turbopack) 1.383s 2.006s 0.623s 15 1.09x
💻 Local Express 1.422s (+0.5%) 2.005s (~) 0.583s 15 1.12x
💻 Local Nitro 1.430s (~) 2.004s (~) 0.575s 15 1.13x
🐘 Postgres Express 2.078s (-3.6%) 2.680s (+3.3%) 0.602s 12 1.64x
🌐 MongoDB Next.js (Turbopack) 2.159s 3.009s 0.849s 10 1.70x
🐘 Postgres Next.js (Turbopack) 2.220s 2.740s 0.520s 11 1.75x
🐘 Postgres Nitro 2.227s (+6.7% 🔺) 2.679s (~) 0.453s 12 1.75x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.952s (+0.9%) 4.097s (-1.9%) 1.145s 8 1.00x
▲ Vercel Express 3.215s (+16.3% 🔺) 4.615s (+8.8% 🔺) 1.400s 7 1.09x
▲ Vercel Nitro 3.777s (+28.7% 🔺) 5.319s (+19.9% 🔺) 1.542s 6 1.28x

🔍 Observability: Next.js (Turbopack) | Express | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 2.493s 3.008s 0.515s 10 1.00x
💻 Local Next.js (Turbopack) 2.583s 3.108s 0.525s 10 1.04x
💻 Local Nitro 2.689s (+2.1%) 3.007s (~) 0.318s 10 1.08x
💻 Local Express 2.719s (+2.9%) 3.008s (~) 0.288s 10 1.09x
🌐 MongoDB Next.js (Turbopack) 4.823s 5.178s 0.354s 6 1.93x
🐘 Postgres Nitro 10.891s (-6.8% 🟢) 11.031s (-8.4% 🟢) 0.140s 3 4.37x
🐘 Postgres Express 11.649s (+13.4% 🔺) 12.035s (+16.2% 🔺) 0.386s 3 4.67x
🐘 Postgres Next.js (Turbopack) 12.917s 13.368s 0.452s 3 5.18x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.901s (-10.4% 🟢) 4.333s (-10.8% 🟢) 1.432s 8 1.00x
▲ Vercel Express 3.090s (-10.6% 🟢) 4.075s (-13.6% 🟢) 0.985s 8 1.07x
▲ Vercel Next.js (Turbopack) 4.030s (+23.2% 🔺) 5.271s (+12.2% 🔺) 1.242s 6 1.39x

🔍 Observability: Nitro | Express | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 4.014s 4.581s 0.567s 7 1.00x
💻 Local Next.js (Turbopack) 6.877s 7.515s 0.637s 4 1.71x
💻 Local Nitro 7.845s (+2.6%) 8.018s (~) 0.174s 4 1.95x
💻 Local Express 7.899s (+3.5%) 8.020s (~) 0.120s 4 1.97x
🌐 MongoDB Next.js (Turbopack) 9.941s 10.681s 0.740s 3 2.48x
🐘 Postgres Nitro 49.331s (-5.4% 🟢) 50.118s (-5.7% 🟢) 0.787s 1 12.29x
🐘 Postgres Express 51.247s (-0.5%) 52.107s (~) 0.860s 1 12.77x
🐘 Postgres Next.js (Turbopack) 52.253s 53.118s 0.865s 1 13.02x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.663s (+10.4% 🔺) 5.136s (-3.4%) 1.473s 6 1.00x
▲ Vercel Next.js (Turbopack) 3.900s (+5.5% 🔺) 5.471s (~) 1.571s 6 1.06x
▲ Vercel Nitro 4.225s (~) 6.046s (-1.9%) 1.821s 5 1.15x

🔍 Observability: Express | Next.js (Turbopack) | Nitro

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Next.js (Turbopack) 0.114s 1.001s 0.009s 1.014s 0.900s 10 1.00x
🌐 Redis Next.js (Turbopack) 0.143s 1.000s 0.001s 1.007s 0.863s 10 1.26x
💻 Local Nitro 0.168s (-0.9%) 1.002s (~) 0.010s (-7.6% 🟢) 1.015s (~) 0.847s 10 1.47x
💻 Local Express 0.172s (~) 1.002s (~) 0.010s (+3.0%) 1.016s (~) 0.843s 10 1.51x
🌐 MongoDB Next.js (Turbopack) 0.507s 0.942s 0.001s 1.009s 0.501s 10 4.46x
🐘 Postgres Express 1.331s (-40.1% 🟢) 1.760s (-37.6% 🟢) 0.001s (-8.3% 🟢) 2.013s (-33.2% 🟢) 0.681s 10 11.70x
🐘 Postgres Next.js (Turbopack) 1.439s 1.603s 0.001s 2.012s 0.574s 10 12.64x
🐘 Postgres Nitro 2.318s (+7.3% 🔺) 2.724s (-5.5% 🟢) 0.001s (+18.2% 🔺) 3.015s (~) 0.697s 10 20.37x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.788s (-0.6%) 3.433s (-3.0%) 0.131s (-41.6% 🟢) 4.404s (-4.3%) 1.615s 10 1.00x
▲ Vercel Nitro 2.810s (+3.7%) 3.340s (-5.7% 🟢) 0.150s (+10.1% 🔺) 4.259s (-6.1% 🟢) 1.448s 10 1.01x
▲ Vercel Next.js (Turbopack) 2.895s (+7.6% 🔺) 3.211s (~) 0.198s (-30.0% 🟢) 4.299s (-3.3%) 1.405s 10 1.04x

🔍 Observability: Express | Nitro | Next.js (Turbopack)

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Next.js (Turbopack) 11/12
🐘 Postgres Express 5/12
▲ Vercel Express 6/12
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 💻 Local 10/12
Next.js (Turbopack) 🌐 Redis 6/12
Nitro 💻 Local 10/12
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)

📋 View full workflow run

@github-actions
Copy link
Contributor

github-actions bot commented Feb 10, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 490 0 38 528
❌ 💻 Local Development 377 41 62 480
✅ 📦 Local Production 418 0 62 480
✅ 🐘 Local Postgres 418 0 62 480
✅ 🪟 Windows 45 0 3 48
❌ 🌍 Community Worlds 106 41 9 156
✅ 📋 Other 123 0 21 144
Total 1977 82 257 2316

❌ Failed Tests

💻 Local Development (41 failed)

nuxt-stable (41 failed):

  • addTenWorkflow
  • addTenWorkflow
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • webhook route with invalid token
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • stepDirectCallWorkflow - calling step functions directly outside workflow context
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check endpoint (HTTP) - workflow and step endpoints respond to __health query parameter
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • health check (CLI) - workflow health command reports healthy endpoints
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
🌍 Community Worlds (41 failed)

turso (41 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • nullByteWorkflow
  • workflowAndStepMetadataWorkflow
  • fetchWorkflow
  • promiseRaceStressTestWorkflow
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • hookCleanupTestWorkflow - hook token reuse after workflow completion
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars)
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument
  • closureVariableWorkflow - nested step functions with closure variables
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly
  • Calculator.calculate - static workflow method using static step methods from another class
  • AllInOneService.processNumber - static workflow method using sibling static step methods
  • ChainableService.processWithThis - static step methods using this to reference the class
  • thisSerializationWorkflow - step function invoked with .call() and .apply()
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE
  • instanceMethodStepWorkflow - instance methods with "use step" directive
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 44 0 4
✅ example 44 0 4
✅ express 44 0 4
✅ fastify 44 0 4
✅ hono 44 0 4
✅ nextjs-turbopack 47 0 1
✅ nextjs-webpack 47 0 1
✅ nitro 44 0 4
✅ nuxt 44 0 4
✅ sveltekit 44 0 4
✅ vite 44 0 4
❌ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 41 0 7
✅ express-stable 41 0 7
✅ fastify-stable 41 0 7
✅ hono-stable 41 0 7
✅ nextjs-turbopack-stable 45 0 3
✅ nextjs-webpack-stable 45 0 3
✅ nitro-stable 41 0 7
❌ nuxt-stable 0 41 7
✅ sveltekit-stable 41 0 7
✅ vite-stable 41 0 7
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 41 0 7
✅ express-stable 41 0 7
✅ fastify-stable 41 0 7
✅ hono-stable 41 0 7
✅ nextjs-turbopack-stable 45 0 3
✅ nextjs-webpack-stable 45 0 3
✅ nitro-stable 41 0 7
✅ nuxt-stable 41 0 7
✅ sveltekit-stable 41 0 7
✅ vite-stable 41 0 7
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 41 0 7
✅ express-stable 41 0 7
✅ fastify-stable 41 0 7
✅ hono-stable 41 0 7
✅ nextjs-turbopack-stable 45 0 3
✅ nextjs-webpack-stable 45 0 3
✅ nitro-stable 41 0 7
✅ nuxt-stable 41 0 7
✅ sveltekit-stable 41 0 7
✅ vite-stable 41 0 7
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 45 0 3
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 3 0 0
✅ mongodb 45 0 3
✅ redis-dev 3 0 0
✅ redis 45 0 3
✅ starter-dev 3 0 0
✅ turso-dev 3 0 0
❌ turso 4 41 3
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 41 0 7
✅ e2e-local-postgres-nest-stable 41 0 7
✅ e2e-local-prod-nest-stable 41 0 7

📋 View full workflow run


Some E2E test jobs failed:

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

Check the workflow run for details.

Copy link
Contributor

@vercel vercel bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

Missing requestId handler in attributeToDisplayFn causes TypeScript error TS2741 and breaks the build after requestId was added to EventSchema.

Fix on Vercel

Copy link
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

Adds end-to-end propagation and persistence of Vercel request IDs (x-vercel-id) so workflow/step events can be correlated with request-level logs in observability tooling.

Changes:

  • Extends event and queue metadata types to carry an optional requestId, and threads it through core runtime event creation calls.
  • Extracts x-vercel-id in Vercel and local queue handlers and propagates it into queue callback metadata (via AsyncLocalStorage for @vercel/queue).
  • Persists requestId on events in Postgres and local file event storage, and forwards it through the Vercel API client.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
packages/world/src/queue.ts Adds requestId?: string to queue handler metadata type.
packages/world/src/events.ts Adds requestId?: string to CreateEventParams and to EventSchema.
packages/world-vercel/src/queue.ts Captures x-vercel-id from incoming requests and propagates via AsyncLocalStorage.
packages/world-vercel/src/events.ts Forwards requestId in v2 event creation requests.
packages/world-postgres/src/storage.ts Writes requestId into event inserts and includes it in returned event objects.
packages/world-postgres/src/drizzle/schema.ts Adds request_id column to the Drizzle events table schema.
packages/world-local/src/storage/events-storage.ts Includes requestId when writing local event JSON.
packages/world-local/src/queue.ts Extracts x-vercel-id and passes it into handler metadata.
packages/core/src/runtime/suspension-handler.ts Propagates requestId into suspension-created events.
packages/core/src/runtime/step-handler.ts Propagates requestId into step lifecycle events.
packages/core/src/runtime/start.ts Adds StartOptions.requestId and passes it into run_created event creation.
packages/core/src/runtime.ts Propagates requestId into workflow lifecycle events and suspension handling.
.changeset/request-id-tracking.md Declares patch releases for the affected packages.

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

Comment on lines 106 to +108
specVersion: integer('spec_version'),
/** The Vercel request ID (x-vercel-id header) that created this event */
requestId: varchar('request_id'),
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

A Drizzle migration needs to be added for this new request_id column. The package’s db:push script runs drizzle-orm migrations (see src/cli.ts), so without a new migrations/*.sql entry that ALTERs workflow.workflow_events to add request_id, installs/upgrades will fail at runtime when inserts include requestId.

Copilot uses AI. Check for mistakes.
// Wrap the VQS handler to extract x-vercel-id from the incoming request
// and propagate it via AsyncLocalStorage into the handler callback
return async (req: Request) => {
const requestId = req.headers.get(VERCEL_REQUEST_ID_HEADER) ?? undefined;
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Header extraction uses req.headers.get(...) ?? undefined, which preserves an empty header value as ''. That then propagates as a (falsy) requestId through metadata and can lead to inconsistent persistence/serialization downstream. Consider normalizing here (e.g., trim and convert '' to undefined) so the rest of the pipeline can treat “missing/blank” consistently.

Suggested change
const requestId = req.headers.get(VERCEL_REQUEST_ID_HEADER) ?? undefined;
const rawRequestId = req.headers.get(VERCEL_REQUEST_ID_HEADER);
const requestId = rawRequestId?.trim() || undefined;

Copilot uses AI. Check for mistakes.
Comment on lines 193 to 207
// Extract x-vercel-id for request ID tracking (available on Vercel deployments)
const requestId = req.headers.get('x-vercel-id') ?? undefined;

if (!queueName.startsWith(prefix)) {
return Response.json({ error: 'Unhandled queue' }, { status: 400 });
}

const body = await new JsonTransport().deserialize(req.body);
try {
const result = await handler(body, { attempt, queueName, messageId });
const result = await handler(body, {
attempt,
queueName,
messageId,
requestId,
});
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Header extraction uses req.headers.get('x-vercel-id') ?? undefined, which keeps an empty header value as ''. Since downstream code often gates on truthiness, this can result in the requestId being dropped from emitted events even though it was “present”. Consider normalizing (trim + convert blank to undefined) before passing it into the handler metadata.

Copilot uses AI. Check for mistakes.
Comment on lines 393 to 397
eventType: data.eventType,
eventData: 'eventData' in data ? data.eventData : undefined,
specVersion: effectiveSpecVersion,
requestId: params?.requestId ?? null,
})
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

This change adds new persisted/returned behavior (requestId stored on events). There are extensive integration tests in packages/world-postgres/test/storage.test.ts, but none currently assert requestId round-trips. Adding at least one test that calls events.create(..., ..., { requestId }) and verifies the returned event (and/or a subsequent list/get) includes the same requestId would prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines 396 to 406
requestId: params?.requestId ?? null,
})
.returning({ createdAt: Schema.events.createdAt });

const result = { ...data, ...value, runId: effectiveRunId, eventId };
const result = {
...data,
...value,
runId: effectiveRunId,
eventId,
...(params?.requestId ? { requestId: params.requestId } : {}),
};
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

params?.requestId is written to the DB using ?? null, but the returned event object only includes requestId when it’s truthy (params?.requestId ? ...). This can create inconsistencies (e.g., an empty-string requestId is persisted but omitted from the returned event) and makes behavior dependent on truthiness. Consider normalizing requestId (treat blank as undefined) and using an explicit undefined/null check when deciding whether to include it in the result.

Copilot uses AI. Check for mistakes.
Comment on lines 858 to 878
@@ -866,6 +874,7 @@ export function createEventsStorage(drizzle: Drizzle): Storage['events'] {
...conflictValue,
runId: effectiveRunId,
eventId,
...(params?.requestId ? { requestId: params.requestId } : {}),
};
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

Same truthiness issue as other result constructions: requestId is persisted via ?? null, but then only included in the returned conflictResult when params?.requestId is truthy. Prefer an explicit undefined/null check (or normalize blank to undefined earlier) so storage and returned event stay consistent.

Copilot uses AI. Check for mistakes.
...value,
runId: effectiveRunId,
eventId,
...(params?.requestId ? { requestId: params.requestId } : {}),
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

requestId is always inserted (as null when missing), but the returned result only includes it when truthy. This can drop valid-but-falsy values (e.g., empty string) and makes behavior inconsistent across code paths. Consider normalizing the input and switching to an explicit undefined/null check when adding requestId to the returned event object.

Suggested change
...(params?.requestId ? { requestId: params.requestId } : {}),
...(params?.requestId !== undefined
? { requestId: params.requestId ?? null }
: {}),

Copilot uses AI. Check for mistakes.
Comment on lines 136 to 149
const result = await world.events.create(
runId,
{
eventType: 'run_created',
specVersion,
eventData: {
deploymentId: deploymentId,
workflowName: workflowName,
input: workflowArguments,
executionContext: { traceCarrier, workflowCoreVersion },
},
},
{ v1Compat }
{ v1Compat, requestId: opts?.requestId }
);
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

StartOptions adds requestId and threads it into world.events.create(...). There are existing unit tests for start() in packages/core/src/runtime/start.test.ts, but none currently assert that options are forwarded into the create-event params. Adding a test that passes requestId and verifies the mocked world.events.create receives it would lock in this behavior.

Copilot uses AI. Check for mistakes.
eventId,
createdAt: now,
specVersion: effectiveSpecVersion,
...(params?.requestId ? { requestId: params.requestId } : {}),
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

requestId is added to the stored event only when it’s truthy. If an empty-string requestId slips through (e.g., a present-but-blank header), it will be silently dropped, making behavior differ from “explicitly provided but blank”. Consider normalizing requestId earlier (trim + convert blank to undefined) and/or switching this condition to an explicit undefined/null check for consistency with other storages.

Suggested change
...(params?.requestId ? { requestId: params.requestId } : {}),
...(params?.requestId !== undefined && params?.requestId !== null
? { requestId: params.requestId }
: {}),

Copilot uses AI. Check for mistakes.
- Add requestId to attributeToDisplayFn in web-shared (fixes TS2741 build error)
- Add Drizzle migration 0007 for request_id column on workflow_events
- Normalize empty-string requestId values in queue handlers (trim + falsy check)
- Use consistent || null for requestId persistence in postgres storage
- Include @workflow/web-shared in changeset

https://claude.ai/code/session_01TqomNcQoixA1HzxvFFBsu2
@pranaygp
Copy link
Collaborator Author

gonna spend a bit more time thinking through this flow properly before re-opening it out of draft

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