Cloud VMs: trace ids end to end, every error to Sentry and PostHog, latency on every request
Nightly Cloud VM failures were hard to debug: the Mac client sent no trace context and recorded nothing, the server returned no trace id, Sentry saw only operator faults, PostHog saw only create-like failures, and provider spans recorded the ProviderError wrapper instead of the Freestyle status code. This PR gives every Cloud VM request one trace id shared by the client, Axiom, PostHog and Sentry, and records every error and every latency.
Server (web/). withAuthedVmApiRoute owns one request context per call (route, operation, client identity from X-Cmux-* headers and traceparent, Vercel request id, user id, last error) and a finalizer that runs on every VM route. Every response carries x-cmux-trace-id and x-cmux-span-id; every error body carries traceId. The finalizer stamps success, duration, error code, user and client on the span, sends PostHog cloud_vm_request for every failure and for the successes a user waits on (polled reads succeed silently), sends a PostHog $exception per failure fingerprinted by error code, and force-flushes the tracer in after() so an error-heavy instance stops dropping the trace the PostHog row points at. reportError gained level, tags and a trace context: every VM error now reaches Sentry, operator faults as error, user faults as warning, tagged with trace_id. recordSpanError walks cause so provider spans record the inner HTTP status and code. cloud_vm_provision keeps schema 2 and gains trace_id, duration_ms, error_phase and client fields, so the existing alert insight keeps working.
Mac app. VMClient.request() is the single choke point for the control plane. It mints a W3C traceparent and X-Cmux-Client-Request-Id, sends X-Cmux-Client, X-Cmux-App-Version, X-Cmux-App-Build, X-Cmux-Channel, measures latency, and records the outcome through the new VMClientTelemetry: os.log category CloudVM and a Sentry breadcrumb for every request, PostHog cmux_cloud_vm_request for failures and non-polled successes, a Sentry event for failures (5xx and transport failures error, 4xx warning), throttled per operation and code. Every Cloud VM error message ends with a localized Reference: <trace id> line and the socket vm_error payload carries data.trace_id, so the CLI shows it too.
Verification: bun test for vm-request-telemetry, vm-observability, vm-route-auth, telemetry-sampler, vm-timings, observability-alerts, coderouter-sentry, vm-alerts pass; tsgo and eslint clean. vm-auth-cache + vm-route-auth fail when run in one process on main as well (leaked setSystemTime throttle state), unrelated. Tagged build cvobs compiles on the fleet; VMClientTelemetryTests dispatched on test-e2e.yml. Runbook added to web/services/vms/README.md (Telemetry section).
Decisions to confirm: successes of non-polled operations now reach PostHog with latency (the 2026-08-27 split kept all successes out); user-fault 4xx now reach Sentry as warnings.
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.
Summary by cubic
Gives every Cloud VM request one trace id shared across the Mac client, Axiom, PostHog, and Sentry, and records every error and latency so nightly failures are debuggable.
Behavior changes
- Non-polled operation successes now reach PostHog with latency; previously all successes were excluded.
- User-fault 4xx errors now reach Sentry as warnings; previously only operator faults did.
What changed
- Server responses carry
x-cmux-trace-id/x-cmux-span-id, error bodies carrytraceId, and every error message ends with a localizedReference: <trace id>line. - The Mac client mints a W3C
traceparentper request and records latency and outcome via the newVMClientTelemetry. - Every VM error reaches Sentry (operator faults
error, user faultswarning) and PostHog (cloud_vm_requestplus$exceptionper failure, fingerprinted by code). - Provider spans now record the inner HTTP status and code from the cause chain instead of the
ProviderErrorwrapper. cloud_vm_provisionkeeps schema 2 and gainstrace_id,duration_ms,error_phase, and client fields.- Error-heavy instances force a bounded trace flush in
after()so the trace a PostHog row points at is not dropped.