Skip to content

Show why hosted requests and cron jobs fail - #395

Merged
Brooooooklyn merged 4 commits into
mainfrom
fix/observability-ux
Oct 8, 2026
Merged

Brooooooklyn merged 4 commits into
mainfrom
fix/observability-ux

Conversation

@Brooooooklyn

@Brooooooklyn Brooooooklyn commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Problem

A hosted app (napi-rs) returned about 100 worker 500s in one day. The 5xx alert email pointed to void project requests and void project logs. Neither command showed the cause:

$ void project requests --status 5xx
16:30:14 GET  500 static 1897ms        <- the worker answered, but the label says "static"

$ void project logs --level error
16:30:12 GET https://napi.rs/sponsors.svg 500
  [error] TypeError: Cannot read properties of undefined (reading 'href')
                                        <- no stack, although the API returns it

13:00:25 POST https://internal/__void/scheduled 500
  [error] AggregateError: Scheduled jobs failed
                                        <- which job failed, and why, is lost

Three causes:

request label   dispatch: a path with a file extension (/sponsors.svg) stays "static",
                even when the app's own route answered it
logs stack      CLI: prints only `Name: message`; drops errorInfo[].stack and exception.stack
cron error      runtime: Workers log capture keeps an error's own message and stack,
                but not AggregateError.errors, so the inner errors vanish

What this PR changes

request label (dispatch metering)
  dotted path on an SSR deploy, no platform failure
    ├─ manifest says "not a file" and the worker answers the path ─> ssr
    ├─ worker owns the path and returns 5xx or times out ─────────> ssr
    └─ listed file, asset-layer answer, platform asset failure ───> static (unchanged)
  The ISR write-back checks the path directly, so these routes never enter the ISR cache.

void project logs
  [error] TypeError: Cannot read properties of undefined (reading 'href')
              at assets/resvg-gkFxLqVH.js:14881:64      <- dimmed frames under the
              at assets/resvg-gkFxLqVH.js:15409:3          argument that logged them
  [exception] frames print the same way

scheduled jobs
  job fails ─> console.error('[void] Scheduled job crons/x.ts failed for cron "0 5 * * *":', error)
              (logged at once, with the original error, so its stack is kept)
  all jobs done ─> throw AggregateError(errors, 'Scheduled jobs failed: crons/x.ts')
              (HTTP 500 and the shared onError are unchanged)

Real output from this branch's CLI against the live napi-rs data:

15:23:32 GET https://napi.rs/sponsors.svg 500
  [error] TypeError: Cannot read properties of undefined (reading 'href')
              at assets/resvg-gkFxLqVH.js:14881:64
              at assets/resvg-gkFxLqVH.js:15409:3
              at new Promise (<anonymous>)
              at assets/resvg-gkFxLqVH.js:15408:19
              at assets/client-7iHZB5sW.js:16:49

Decisions

Question Decision Why
How does dispatch know the worker answered? Reuse the asset-manifest answer it already reads for the static cache No new KV read on the hot path
New request type such as worker? No, use ssr The CLI, docs and usage queries already know ssr
Fix cron errors in the shared Hono onError? No, in the scheduled runner onError serves every route; the runner knows the job file and cron
Change the platform API or tail worker for stacks? No, CLI only The API already returns errorInfo[].stack and exception.stack
void platform deployment logs (operator command) Not changed Outside the reported problem

Rollout

merge ─> release void ──> users upgrade the CLI ──────> stacks print     (no redeploy needed)
                     └──> apps redeploy ──────────────> cron errors named
       deploy hosted dispatch ─────────────────────────> new rows labeled ssr
       (old Analytics rows keep "static")

Known limits

  • Some app routes on paths with a file extension can still be labeled static when they succeed or return 4xx. This applies to requests with a Cookie or Authorization header, to HEAD, Range and POST requests, and to SSR apps that uploaded no static files. On framework deploys, these requests also stay static when they fail with a 5xx. A correct label would need an asset-manifest read on every credentialed asset request. That is too much cost for a display label. On Void SSR apps, server errors are labeled ssr for every request shape.
  • A failed cron now shows two log lines on the hosted path: the per-job line and the AggregateError line.
  • Framework cron runners and queue runners have the same error-loss pattern. They are not changed here.

Testing

Check Result
New tests on old code fail (dispatch 7+, logs rendering 1, scheduled dispatch 2)
Changed test files 5 files, 353 tests pass
vp run test --base origin/main 475 files, 10329 tests pass
vp lint, vp run check pass (31 typecheck tasks)
vp test run 689 files pass. 1 file failed on a port race in the Solid dev-server test: the test waited on a port that vp dev found busy. It passes on rerun. The race is fixed in #396.
Branch CLI against live napi-rs logs frames print under every error
vp dev fixture with one failing cron job per-job line with file, cron, message and stack; the other job runs; HTTP 500

🤖 Generated with Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-08T13:50:28.191303Z 9f1309d New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://fix-observability-ux-void-cloud.voidzero-docs.workers.dev (commit 9f1309d)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://3e4b0a2e-void-cloud.voidzero-docs.workers.dev 9f1309d 2026-10-08T13:48:35.796Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://f928300c-void-cloud.voidzero-docs.workers.dev c6f3a74 2026-10-08T10:42:41.394Z Visit the dashboard ↗

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6f3a748a2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread platform/packages/dispatch/src/index.ts
Brooooooklyn and others added 4 commits October 8, 2026 21:17
On SSR deploys, `void project requests` showed every worker answer to a
path like /sponsors.svg as `static`, including the route's own 500s, which
pointed users away from their worker logs. Dispatch now labels such a path
`ssr` when the user worker answered it and the asset manifest has no file
for it, or the worker returned a server error or ran past the dispatch
budget. Listed files, answers from Cloudflare's asset layer and platform
asset failures stay `static`. The ISR write-back now checks the path
directly, so these routes never enter the ISR cache.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The platform already returns the stack of every logged Error and uncaught
exception, but the CLI printed only `Name: message`. Each Error argument now
has its frames printed under it (dimmed on a terminal), and exceptions show
their frames too. Frames are app data, so they go through the same terminal
control-sequence filter as the log text. The docs explain that passing the
Error object itself to console.error() is what makes its stack appear.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A failing cron logged only `AggregateError: Scheduled jobs failed`, because
Workers log capture keeps an error's own message and stack but not its inner
errors. Each job that fails, at import or while running, now logs its
project-relative file, the cron and the original error as soon as it fails.
The thrown AggregateError still waits for every job, keeps the original
errors and now names the failed files in its message.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Deploys published before asset manifests existed may still have an asset
layer, so the worker-ownership check is used for successful responses only
together with a manifest answer.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Brooooooklyn
Brooooooklyn merged commit 45461ba into main Oct 8, 2026
20 checks passed
@Brooooooklyn
Brooooooklyn deleted the fix/observability-ux branch October 8, 2026 13: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.

1 participant