Skip to content

fix(web): docs locale middleware serves a language refused with q=0 — localeFromAcceptLanguage has no refusal or malformed-q handling (#1309's unfixed sibling) #1316

Closed

Description

@dezren39

Problem

The docs site's locale middleware serves a language the client explicitly refused with q=0, and a malformed q value poisons its sort — the exact bug class fixed for the console in #1309, in a second, unfixed copy of the same Accept-Language parsing logic.

Evidence (main 07eab62f38)

packages/web/src/middleware.ts, localeFromAcceptLanguage (lines 52–84):

  • Line 66: q: q ? Number.parseFloat(q) : 1 — no finiteness check, so q=abc becomes NaN and the comparator at line 73 (b.q - a.q) returns NaN, which sort treats as 0: the refused/malformed entry keeps its original position instead of sorting last.
  • Lines 76–80: the parsed items are mapped to locales and the first match is returned — there is no refusal skip, so an entry with an explicit finite q=0 (or q=0.0, or negative) is still served if it is the first matching locale.
  • Lines 86–97 (onRequest): the result drives a real user-visible redirect — a /docs or /docs/ request is 302-redirected to /docs/<locale>/ (with an oc_locale cookie) based on this function.

The console's copy was fixed in #1309 (PR #1312, merged): packages/console/app/src/lib/language.ts detectFromAcceptLanguage (lines 267–303) now treats a malformed q as 0-weight last-resort and an explicit finite q<=0 as a refusal that is never served. The web middleware copy predates / escaped that fix.

Reproduction (laptop probe, this run)

Probe ~/git/fleet/probe-2026-11-01-web-accept-language/probe.ts on the laptop runs the middleware's function verbatim (with matchLocale stubbed to the real mapping in packages/web/src/i18n/locales.ts for the tested tags):

Accept-Language Result Expected
fr;q=0 fr root — fr was refused
fr;q=0, de;q=0 fr root — both refused
de;q=0.0, es;q=0 de root — both refused
fr;q=0, de;q=0.5 de de (control, passes)
fr fr fr (control, passes)
fr;q=abc, de;q=0.5 fr de (or fr only as last resort) — NaN must not poison the sort

No test covers this function: packages/web has src/i18n/locales.test.ts, src/lib/share-data.test.ts, and src/pages/s/_share-model-param.test.ts, but nothing for the middleware's Accept-Language handling, and localeFromAcceptLanguage is not exported.

Why it matters

q=0 in Accept-Language means "not acceptable" (RFC 9110 §12.4.2). A user (or a crawler / privacy tool) that explicitly refuses a language is nonetheless redirected into that language's docs and handed a one-year oc_locale cookie pinning the refused choice. Browsers rarely send q=0 for their top language, which is why this survives unnoticed — but the console copy was judged worth fixing in #1309, and the two copies disagreeing means the docs site's behavior depends on which surface a visitor hits.

Acceptance criteria

  • An explicit finite q<=0 language is never served by localeFromAcceptLanguage: parsing falls through to the next accepted language, or to root (no redirect) when none is accepted.
  • A malformed q value is treated as a 0-weight last-resort offer (mirroring fix(console): detectFromAcceptLanguage serves a language the client explicitly refused with q=0 #1309's pinned console semantics), never as NaN in the sort comparator.
  • Normal headers behave exactly as before (controls: bare language defaults to q=1; higher-q accepted language wins).
  • Tests cover the refused, all-refused, malformed-q, and control cases and pass in the packages/web test suite (export the helper or extract it the way packages/web/src/pages/s/_share-model-param.test.ts isolates its logic, if the Astro middleware import blocks direct testing).

Related

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    app/webPackage: web (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Nov 1, 2026
  2. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · state: filed

  3. added
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    queue/claimedA queue worker has claimed the task for this item
    on Nov 1, 2026
  4. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Parked: PR #1317 is open with the fix — localeFromAcceptLanguage extracted to packages/web/src/lib/accept-language.ts mirroring the merged console semantics from #1309/PR #1312 (explicit finite q<=0 = refusal, never served; malformed q = 0-weight last-resort, no NaN in the comparator), with 16 new tests. packages/web suite: 48 pass / 0 fail on the laptop worktree; Prettier clean on the touched files.

    Task 5879 is parked until 2026-11-01T19:38Z for PR #1317 CI + Codex review; on resume it merges if green with no undispositioned Codex findings.

  5. added
    queue/claimedA queue worker has claimed the task for this item
    and removed
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 1, 2026
  6. removed
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    on Nov 1, 2026
  7. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 -- repo-queue-worker-shift-opencode (lane A, task 5879, completed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    app/webPackage: web (pkgs/ or apps/ in this repo)effort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions