Skip to content

fix(docs): stop a wildcard Accept resolving to markdown - #105

Merged
joaopcm merged 1 commit into
mainfrom
t3code/fix-docs-accept
Aug 21, 2026
Merged

fix(docs): stop a wildcard Accept resolving to markdown#105
joaopcm merged 1 commit into
mainfrom
t3code/fix-docs-accept

Conversation

@joaopcm

@joaopcm joaopcm commented Aug 21, 2026

Copy link
Copy Markdown
Owner

docs.propgate.dev rendered with no styles. Every stylesheet, script and font came back as a markdown 404.

GET /_next/static/chunks/0a-6hmdrq391z.css
  Accept: text/css,*/*;q=0.1   →  404  text/markdown; charset=utf-8   ✗
  Accept: */*                  →  404  text/markdown; charset=utf-8   ✗
  Accept: text/html,*/*;q=0.8  →  200  text/css                       ✓

The document itself was fine, because a browser sends text/html first for the page. Only the subresources broke — which is exactly the shape that renders as unstyled HTML.

This was #100's Worker going live for the first time; my #104 fixed the deploy that had been hiding it.

Cause

OFFERED lists markdown, then html, then json. matches() returns true for any candidate when the range is */*, so a wildcard resolved to whichever kind happened to be listed first.

A stylesheet request is text/css at q=1 plus a wildcard at q=0.1. text/css matches nothing we offer, so the wildcard decided it, and the wildcard picked markdown.

negotiateType already defaults a missing header to HTML. A wildcard says the same thing — "any of these is fine" — and was taking a different path.

Fix, in two parts

A wildcard takes the HTML default. Not a reorder of OFFERED: that would have hidden this rather than fixed it, and the next caller passing a different offered list would meet it again. The rule belongs on the wildcard.

isAssetPath bypasses negotiation entirely. /_next/*, and any path whose last segment carries an extension, goes straight to the assets binding. This is the part that makes the class impossible rather than merely fixed: even with the wildcard corrected, a client that genuinely sent Accept: text/markdown for a stylesheet would have been answered with a markdown 404, because negotiation would look for …/0a-6hmdrq391z.css.md. A stylesheet has no markdown twin and never should.

.md paths are the exception — those are the twins, and asking for one by name has to keep working.

Checked after isCatalogPath, so /openapi.json and /v1/* keep their JSON error handling.

Tests

10 new, and I verified every one fails without these changes by disabling each branch in turn:

× serves a stylesheet as itself for a Chrome stylesheet request
× serves a stylesheet as itself even when markdown is asked for by name
× passes a real 404 on an asset through untouched
× gets HTML for a bare wildcard
× treats a bare wildcard as HTML rather than the head of the offered list
× serves HTML for a Chrome stylesheet request
× serves HTML for a Chrome script request
× serves HTML for a Chrome font request
× serves HTML for a Chrome image request
× treats text/* as no preference too

The headers are the real ones, written out verbatim rather than abbreviated — the bug lived in the interaction between a concrete type we do not offer and a low-q wildcard, and a tidied-up header does not have that shape.

The opposite direction is covered too: text/markdown, text/markdown,text/html;q=0.5 and text/markdown;q=0.9 with a low-q wildcard all still resolve to markdown, and text/html,text/markdown;q=0.5 still resolves to HTML.

One incidental thing

*/* inside a JSDoc block closes the comment. The first version of this patch would not parse; the prose now says "wildcard" and a check confirms no block comment in these three files contains the literal.

Checks

pnpm lint, pnpm check, pnpm test --force — 12/12, nothing cached. Docs suite 270 tests across 28 files. verify:worker clean.

After merge

Worth confirming on the deployed site, since the failure was invisible to every local signal:

curl -sI -H 'Accept: text/css,*/*;q=0.1' https://docs.propgate.dev/_next/static/chunks/0a-6hmdrq391z.css
# expect: 200, content-type: text/css

Every stylesheet, script and font on docs.propgate.dev came back as a
markdown 404, so the site rendered with no styles.

A browser asks for a stylesheet with `text/css` plus a wildcard at q=0.1.
`text/css` matches nothing we offer, so the wildcard decided it — and a
wildcard matched whichever kind was first in OFFERED, which is markdown.
negotiateType already defaults a missing header to HTML; a wildcard says the
same thing and now takes the same default. Fixing the wildcard rather than
reordering OFFERED, because a reorder would only move the bug to the next
caller passing a different `offered` list.

Second, structural: isAssetPath sends /_next/* and any path whose last segment
has an extension straight to the assets binding, so an asset cannot be
negotiated even by a client asking for markdown by name. A stylesheet has no
markdown twin and never should. Checked after isCatalogPath, so /openapi.json
and /v1/* keep their JSON handling.

10 new tests, all of which fail without these two changes. They use the real
Chrome headers for documents, stylesheets, scripts, fonts and images, plus the
bare wildcard curl sends — that last one is how anybody first checks whether a
deploy worked.

Also: `*/*` inside a JSDoc block closes the comment. The prose says "wildcard".

@greptile-apps greptile-apps 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.

Your trial has ended. Reactivate Greptile to resume code reviews.

@joaopcm joaopcm self-assigned this Aug 21, 2026
@joaopcm
joaopcm merged commit 19958c8 into main Aug 21, 2026
14 checks passed
@joaopcm
joaopcm deleted the t3code/fix-docs-accept branch August 21, 2026 23:32
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