Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Track Vercel request IDs on workflow events for observability#997

Draft
pranaygp wants to merge 1 commit intomainfrom
claude/add-request-id-tracking-LcNAm
Draft

Track Vercel request IDs on workflow events for observability#997
pranaygp wants to merge 1 commit 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

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: e37e4fd

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/core Patch
@workflow/cli Patch
@workflow/web-shared Patch
@workflow/world-postgres 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

@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 🥇 Express 0.032s (-3.9%) 1.005s (~) 0.973s 10 1.00x
💻 Local Nitro 0.033s (+3.7%) 1.005s (~) 0.972s 10 1.03x
💻 Local Next.js (Turbopack) 0.040s 1.005s 0.965s 10 1.24x
🌐 Redis Next.js (Turbopack) 0.050s 1.005s 0.954s 10 1.56x
🌐 MongoDB Next.js (Turbopack) 0.094s 1.007s 0.914s 10 2.90x
🐘 Postgres Next.js (Turbopack) 0.319s 1.009s 0.691s 10 9.86x
🐘 Postgres Express 0.365s (+205.4% 🔺) 1.010s (~) 0.645s 10 11.30x
🐘 Postgres Nitro 0.494s (+375.4% 🔺) 1.010s (~) 0.516s 10 15.31x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 0.506s (-59.2% 🟢) 1.700s (-29.8% 🟢) 1.195s 10 1.00x
▲ Vercel Next.js (Turbopack) 0.548s (-27.4% 🟢) 1.804s (-15.0% 🟢) 1.256s 10 1.08x
▲ Vercel Express 0.565s (-7.2% 🟢) 2.045s (-6.5% 🟢) 1.480s 10 1.12x

🔍 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.091s 2.005s 0.914s 10 1.00x
💻 Local Express 1.105s (~) 2.006s (~) 0.901s 10 1.01x
💻 Local Nitro 1.107s (~) 2.006s (~) 0.899s 10 1.01x
🌐 Redis Next.js (Turbopack) 1.112s 2.006s 0.894s 10 1.02x
🌐 MongoDB Next.js (Turbopack) 1.308s 2.008s 0.700s 10 1.20x
🐘 Postgres Next.js (Turbopack) 1.694s 2.012s 0.317s 10 1.55x
🐘 Postgres Express 2.237s (-6.5% 🟢) 3.015s (~) 0.778s 10 2.05x
🐘 Postgres Nitro 2.358s (-4.9%) 3.014s (~) 0.656s 10 2.16x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.187s (-3.5%) 3.260s (~) 1.073s 10 1.00x
▲ Vercel Nitro 2.199s (-1.3%) 2.868s (-2.1%) 0.668s 10 1.01x
▲ Vercel Next.js (Turbopack) 2.757s (+23.7% 🔺) 3.534s (+12.4% 🔺) 0.777s 10 1.26x

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

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 10.716s 11.022s 0.306s 3 1.00x
💻 Local Next.js (Turbopack) 10.732s 11.022s 0.290s 3 1.00x
💻 Local Express 10.832s (~) 11.023s (~) 0.191s 3 1.01x
💻 Local Nitro 10.836s (~) 11.024s (~) 0.188s 3 1.01x
🌐 MongoDB Next.js (Turbopack) 12.256s 13.019s 0.763s 3 1.14x
🐘 Postgres Next.js (Turbopack) 14.664s 15.042s 0.378s 2 1.37x
🐘 Postgres Express 20.086s (+26.6% 🔺) 21.056s (+27.2% 🔺) 0.970s 2 1.87x
🐘 Postgres Nitro 20.227s (-0.6%) 21.061s (~) 0.834s 2 1.89x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 17.369s (-4.4%) 18.347s (-2.5%) 0.978s 2 1.00x
▲ Vercel Express 17.569s (+1.8%) 19.199s (+4.9%) 1.630s 2 1.01x
▲ Vercel Next.js (Turbopack) 17.599s (-2.6%) 18.793s (-3.7%) 1.194s 2 1.01x

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

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 26.944s 27.052s 0.109s 3 1.00x
💻 Local Next.js (Turbopack) 27.214s 28.051s 0.837s 3 1.01x
💻 Local Express 27.529s (~) 28.052s (~) 0.523s 3 1.02x
💻 Local Nitro 27.543s (~) 28.053s (~) 0.510s 3 1.02x
🌐 MongoDB Next.js (Turbopack) 30.569s 31.048s 0.479s 2 1.13x
🐘 Postgres Next.js (Turbopack) 32.254s 32.580s 0.327s 2 1.20x
🐘 Postgres Nitro 38.077s (-24.4% 🟢) 38.602s (-24.5% 🟢) 0.525s 2 1.41x
🐘 Postgres Express 50.358s (+33.7% 🔺) 51.137s (+34.2% 🔺) 0.779s 2 1.87x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 43.106s (+1.6%) 44.294s (+2.4%) 1.188s 2 1.00x
▲ Vercel Express 43.249s (-0.6%) 44.232s (-1.7%) 0.982s 2 1.00x
▲ Vercel Next.js (Turbopack) 44.057s (+0.5%) 45.122s (+1.0%) 1.065s 2 1.02x

🔍 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.479s 55.102s 0.623s 2 1.00x
💻 Local Next.js (Turbopack) 56.688s 57.102s 0.414s 2 1.04x
💻 Local Express 57.449s (~) 58.106s (~) 0.657s 2 1.05x
💻 Local Nitro 57.484s (~) 58.104s (~) 0.619s 2 1.06x
🌐 MongoDB Next.js (Turbopack) 60.854s 61.067s 0.213s 2 1.12x
🐘 Postgres Next.js (Turbopack) 62.629s 63.144s 0.515s 2 1.15x
🐘 Postgres Nitro 76.404s (-23.9% 🟢) 76.739s (-24.2% 🟢) 0.335s 2 1.40x
🐘 Postgres Express 100.487s (+36.2% 🔺) 101.236s (+35.6% 🔺) 0.749s 1 1.84x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 86.815s (-1.3%) 87.629s (-1.7%) 0.814s 2 1.00x
▲ Vercel Express 87.135s (-0.6%) 87.820s (-1.1%) 0.685s 2 1.00x
▲ Vercel Next.js (Turbopack) 89.144s (~) 90.321s (~) 1.177s 1 1.03x

🔍 Observability: Nitro | Express | 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.273s 2.006s 0.733s 15 1.00x
💻 Local Next.js (Turbopack) 1.391s 2.006s 0.615s 15 1.09x
💻 Local Express 1.407s (~) 2.006s (~) 0.599s 15 1.11x
💻 Local Nitro 1.434s (+1.5%) 2.006s (~) 0.571s 15 1.13x
🌐 MongoDB Next.js (Turbopack) 2.134s 3.008s 0.874s 10 1.68x
🐘 Postgres Express 2.182s (-2.1%) 2.597s (-3.1%) 0.415s 12 1.71x
🐘 Postgres Nitro 2.189s (-9.6% 🟢) 3.015s (~) 0.825s 10 1.72x
🐘 Postgres Next.js (Turbopack) 2.202s 2.679s 0.476s 12 1.73x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.479s (-14.5% 🟢) 3.138s (-17.9% 🟢) 0.658s 10 1.00x
▲ Vercel Nitro 2.571s (+6.8% 🔺) 3.503s (+12.8% 🔺) 0.932s 9 1.04x
▲ Vercel Express 2.579s (-9.1% 🟢) 3.559s (-7.1% 🟢) 0.981s 9 1.04x

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

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 2.480s 3.008s 0.528s 10 1.00x
💻 Local Next.js (Turbopack) 2.482s 3.007s 0.525s 10 1.00x
💻 Local Nitro 2.615s (+1.9%) 3.007s (~) 0.393s 10 1.05x
💻 Local Express 2.692s (+4.4%) 3.007s (~) 0.315s 10 1.09x
🌐 MongoDB Next.js (Turbopack) 4.736s 5.177s 0.441s 6 1.91x
🐘 Postgres Nitro 9.322s (+11.4% 🔺) 10.034s (+14.3% 🔺) 0.712s 3 3.76x
🐘 Postgres Express 10.000s (-15.8% 🟢) 10.700s (-13.5% 🟢) 0.700s 3 4.03x
🐘 Postgres Next.js (Turbopack) 13.135s 13.702s 0.567s 3 5.30x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 3.125s (+26.6% 🔺) 4.059s (+23.6% 🔺) 0.933s 8 1.00x
▲ Vercel Next.js (Turbopack) 3.173s (+6.2% 🔺) 3.938s (+1.7%) 0.765s 8 1.02x
▲ Vercel Nitro 3.615s (+38.3% 🔺) 4.311s (+27.3% 🔺) 0.696s 7 1.16x

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

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 4.023s 4.439s 0.416s 7 1.00x
💻 Local Next.js (Turbopack) 7.301s 8.017s 0.716s 4 1.82x
💻 Local Nitro 7.618s (+4.8%) 8.019s (~) 0.401s 4 1.89x
💻 Local Express 7.989s (+3.2%) 8.521s (+6.3% 🔺) 0.531s 4 1.99x
🌐 MongoDB Next.js (Turbopack) 9.720s 10.349s 0.629s 3 2.42x
🐘 Postgres Nitro 48.231s (+6.8% 🔺) 49.113s (+6.5% 🔺) 0.882s 1 11.99x
🐘 Postgres Express 49.638s (-2.5%) 50.116s (-2.0%) 0.478s 1 12.34x
🐘 Postgres Next.js (Turbopack) 53.804s 54.115s 0.311s 1 13.38x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.253s (-40.1% 🟢) 4.305s (-32.7% 🟢) 1.053s 7 1.00x
▲ Vercel Nitro 4.069s (+19.8% 🔺) 4.822s (-7.9% 🟢) 0.753s 7 1.25x
▲ Vercel Express 4.650s (+29.5% 🔺) 6.112s (+28.7% 🔺) 1.463s 5 1.43x

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

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 1.261s 2.006s 0.745s 15 1.00x
💻 Local Nitro 1.436s (~) 2.005s (~) 0.569s 15 1.14x
💻 Local Express 1.438s (~) 2.006s (~) 0.568s 15 1.14x
💻 Local Next.js (Turbopack) 1.446s 2.004s 0.558s 15 1.15x
🐘 Postgres Express 1.900s (-9.6% 🟢) 2.513s (-3.3%) 0.613s 12 1.51x
🐘 Postgres Nitro 1.985s (-5.4% 🟢) 2.321s (-7.7% 🟢) 0.337s 13 1.57x
🌐 MongoDB Next.js (Turbopack) 2.133s 3.008s 0.875s 10 1.69x
🐘 Postgres Next.js (Turbopack) 2.144s 2.741s 0.598s 11 1.70x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.269s (~) 2.863s (-9.4% 🟢) 0.594s 11 1.00x
▲ Vercel Nitro 2.398s (+3.1%) 3.211s (+4.3%) 0.813s 10 1.06x
▲ Vercel Express 2.554s (+14.2% 🔺) 3.555s (+14.1% 🔺) 1.001s 9 1.13x

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

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🌐 Redis 🥇 Next.js (Turbopack) 2.502s 3.008s 0.506s 10 1.00x
💻 Local Next.js (Turbopack) 2.694s 3.008s 0.313s 10 1.08x
💻 Local Express 2.745s (+1.2%) 3.009s (~) 0.264s 10 1.10x
💻 Local Nitro 2.777s (+3.0%) 3.009s (~) 0.232s 10 1.11x
🌐 MongoDB Next.js (Turbopack) 4.749s 5.176s 0.427s 6 1.90x
🐘 Postgres Nitro 10.574s (-10.9% 🟢) 11.367s (-8.1% 🟢) 0.793s 3 4.23x
🐘 Postgres Express 10.795s (-15.4% 🟢) 11.373s (-14.9% 🟢) 0.578s 3 4.31x
🐘 Postgres Next.js (Turbopack) 12.163s 13.032s 0.869s 3 4.86x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Express 2.510s (-40.7% 🟢) 3.433s (-35.8% 🟢) 0.922s 9 1.00x
▲ Vercel Nitro 3.189s (+33.1% 🔺) 3.899s (+29.2% 🔺) 0.710s 8 1.27x
▲ Vercel Next.js (Turbopack) 3.782s (+25.7% 🔺) 4.598s (+19.7% 🔺) 0.815s 8 1.51x

🔍 Observability: Express | Nitro | 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.061s 4.438s 0.377s 7 1.00x
💻 Local Next.js (Turbopack) 7.803s 8.269s 0.465s 4 1.92x
💻 Local Express 8.252s (+3.4%) 9.021s (+5.9% 🔺) 0.769s 4 2.03x
💻 Local Nitro 8.383s (+7.5% 🔺) 9.024s (+12.6% 🔺) 0.640s 4 2.06x
🌐 MongoDB Next.js (Turbopack) 9.839s 10.347s 0.508s 3 2.42x
🐘 Postgres Express 48.644s (-4.4%) 49.109s (-3.9%) 0.465s 1 11.98x
🐘 Postgres Nitro 51.358s (-2.9%) 52.117s (-1.9%) 0.759s 1 12.65x
🐘 Postgres Next.js (Turbopack) 55.178s 56.118s 0.940s 1 13.59x

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.148s (-4.1%) 4.305s (-2.6%) 1.157s 7 1.00x
▲ Vercel Nitro 3.659s (+30.7% 🔺) 4.785s (+32.5% 🔺) 1.126s 8 1.16x
▲ Vercel Express 4.112s (-19.6% 🟢) 5.029s (-18.5% 🟢) 0.917s 7 1.31x

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

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.147s 1.001s 0.011s 1.016s 0.869s 10 1.00x
🌐 Redis Next.js (Turbopack) 0.150s 1.000s 0.001s 1.007s 0.857s 10 1.02x
💻 Local Express 0.171s (-2.0%) 1.002s (~) 0.012s (+0.9%) 1.017s (~) 0.846s 10 1.16x
💻 Local Nitro 0.176s (+3.1%) 1.003s (~) 0.012s (+2.6%) 1.017s (~) 0.841s 10 1.19x
🐘 Postgres Next.js (Turbopack) 0.457s 0.803s 0.001s 1.010s 0.553s 10 3.10x
🌐 MongoDB Next.js (Turbopack) 0.498s 0.947s 0.002s 1.009s 0.510s 10 3.38x
🐘 Postgres Nitro 1.224s (-49.9% 🟢) 1.886s (-27.5% 🟢) 0.001s (+7.7% 🔺) 2.014s (-33.2% 🟢) 0.790s 10 8.30x
🐘 Postgres Express 2.126s (+143.3% 🔺) 2.909s (+327.8% 🔺) 0.001s (-26.3% 🟢) 3.016s (+198.2% 🔺) 0.889s 10 14.41x

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.909s (-1.6%) 2.451s (-8.0% 🟢) 0.245s (+8.0% 🔺) 3.192s (-7.3% 🟢) 1.282s 10 1.00x
▲ Vercel Nitro 2.005s (+6.3% 🔺) 2.611s (+6.4% 🔺) 0.232s (-13.7% 🟢) 3.311s (+3.4%) 1.306s 10 1.05x
▲ Vercel Express 2.019s (+3.1%) 2.719s (+0.9%) 0.245s (-42.3% 🟢) 3.544s (-3.7%) 1.525s 10 1.06x

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

Summary

Fastest Framework by World

Winner determined by most benchmark wins

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

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 💻 Local 9/12
Next.js (Turbopack) 🌐 Redis 7/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 510 2 38 550
✅ 💻 Local Development 532 0 68 600
✅ 📦 Local Production 532 0 68 600
✅ 🐘 Local Postgres 532 0 68 600
✅ 🪟 Windows 47 0 3 50
❌ 🌍 Community Worlds 107 43 9 159
✅ 📋 Other 129 0 21 150
Total 2389 45 275 2709

❌ Failed Tests

▲ Vercel Production (2 failed)

astro (1 failed):

  • webhookWorkflow

fastify (1 failed):

  • promiseAnyWorkflow
🌍 Community Worlds (43 failed)

turso (43 failed):

  • addTenWorkflow
  • addTenWorkflow
  • should work with react rendering in step
  • promiseAllWorkflow
  • promiseRaceWorkflow
  • promiseAnyWorkflow
  • hookWorkflow
  • webhookWorkflow
  • sleepingWorkflow
  • parallelSleepWorkflow
  • 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 retry behavior workflow completes despite transient 5xx on step_completed
  • 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 45 1 4
✅ example 46 0 4
✅ express 46 0 4
❌ fastify 45 1 4
✅ hono 46 0 4
✅ nextjs-turbopack 49 0 1
✅ nextjs-webpack 49 0 1
✅ nitro 46 0 4
✅ nuxt 46 0 4
✅ sveltekit 46 0 4
✅ vite 46 0 4
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 43 0 7
✅ express-stable 43 0 7
✅ fastify-stable 43 0 7
✅ hono-stable 43 0 7
✅ nextjs-turbopack-canary 47 0 3
✅ nextjs-turbopack-stable 47 0 3
✅ nextjs-webpack-canary 47 0 3
✅ nextjs-webpack-stable 47 0 3
✅ nitro-stable 43 0 7
✅ nuxt-stable 43 0 7
✅ sveltekit-stable 43 0 7
✅ vite-stable 43 0 7
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 43 0 7
✅ express-stable 43 0 7
✅ fastify-stable 43 0 7
✅ hono-stable 43 0 7
✅ nextjs-turbopack-canary 47 0 3
✅ nextjs-turbopack-stable 47 0 3
✅ nextjs-webpack-canary 47 0 3
✅ nextjs-webpack-stable 47 0 3
✅ nitro-stable 43 0 7
✅ nuxt-stable 43 0 7
✅ sveltekit-stable 43 0 7
✅ vite-stable 43 0 7
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 43 0 7
✅ express-stable 43 0 7
✅ fastify-stable 43 0 7
✅ hono-stable 43 0 7
✅ nextjs-turbopack-canary 47 0 3
✅ nextjs-turbopack-stable 47 0 3
✅ nextjs-webpack-canary 47 0 3
✅ nextjs-webpack-stable 47 0 3
✅ nitro-stable 43 0 7
✅ nuxt-stable 43 0 7
✅ sveltekit-stable 43 0 7
✅ vite-stable 43 0 7
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 47 0 3
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 3 0 0
✅ mongodb 47 0 3
✅ redis-dev 3 0 0
✅ redis 47 0 3
✅ turso-dev 3 0 0
❌ turso 4 43 3
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 43 0 7
✅ e2e-local-postgres-nest-stable 43 0 7
✅ e2e-local-prod-nest-stable 43 0 7

📋 View full workflow run


Some E2E test jobs failed:

  • Vercel Prod: failure
  • Local Dev: success
  • 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
specVersion: effectiveSpecVersion,
requestId: params?.requestId ?? null,
})
.returning({ createdAt: events.createdAt });

if (!conflictValue) {
throw new WorkflowAPIError(
`Event ${eventId} could not be created`,
{ status: 409 }
);
}

const conflictResult = {
eventType: 'hook_conflict' as const,
correlationId: data.correlationId,
eventData: conflictEventData,
...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.
@pranaygp
Copy link
Collaborator Author

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

Extract x-vercel-id header and VQS message ID from incoming requests in
queue handlers and pass them through metadata. The step handler forwards
both IDs via CreateEventParams when creating step_started events. The
Vercel world wires these fields to the workflow-server API, which will
store them on the step entity for correlating request logs with step
execution attempts in observability.

Changes:
- Add requestId/messageId to queue handler metadata (world, world-vercel,
  world-local)
- Add requestId/messageId to CreateEventParams (world)
- Pass both IDs in step_started event creation (core)
- Wire requestId/messageId to POST body in Vercel events API
  (world-vercel)

https://claude.ai/code/session_01TqomNcQoixA1HzxvFFBsu2
@pranaygp pranaygp force-pushed the claude/add-request-id-tracking-LcNAm branch from d011706 to e37e4fd Compare February 16, 2026 00:57
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.

3 participants