Skip to content

clef-proxy: data-path upstream fetch has no timeout — a gateway that accepts and never responds holds the /v1/systemone request open (models fetch in the same file pins 10s) #2100

Closed

Description

@dezren39

clef-proxy: data-path upstream fetch has no timeout — a gateway that accepts and never responds holds the /v1/systemone request open (the models fetch in the same file pins 10s)

Evidence

bin/clef-proxy.mjs (main 82b019a0, sha256 12e3415ff346ac25…):

  • :234 — the data path, the proxy's only inference call, awaits the Cloudflare gateway with no signal and no deadline of any kind:
const cfRes = await fetch(cfUrl, {
  method: 'POST',
  headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
  body: JSON.stringify(payload)
});

cfUrl is built at :227 (https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/workers-ai/run/<model>). The follow-up await cfRes.json() at :248 is likewise unbounded, so a gateway that sends headers and then trickles or stalls the body hangs the same handler a second way.

  • :128-130 — the same file already knows the fix pattern: the live-model-catalog fetch pins signal: AbortSignal.timeout(10000). Only the catalog got a deadline; the request that actually serves user traffic did not.

  • The handler is the POST /v1/systemone route: until the fetch settles it never writes a response, so the client connection, the request handler, and (for the hosted SystemOne caller) the turn all stay open. Nothing maps a stall to a 504 — if the runtime eventually errors the fetch, the shared catch at the end of the handler reports it as a generic 500.

Probe (this run, sandbox Node, verbatim call shapes)

A local HTTP server that accepts and never responds, called two ways:

call result
fetch(url, { signal: AbortSignal.timeout(2000) }) (control) rejected after ~2000 ms (TimeoutError)
fetch(url, { method: 'POST', headers, body }) — exactly the :234 shape, no signal still unsettled after 12 s, probe exited with the request pending

So the data path's only bound is whatever the fetch runtime's global defaults happen to be (undici's multi-minute headers/body defaults) — roughly 30× the 10 s deadline this same file chose for its catalog fetch, and not a contract anything pins or tests.

Why it matters

Clef is a decision endpoint: a stalled gateway turns into a stalled caller with no error, no log line naming a timeout, and no response to retry against, for minutes at a time. The proxy process stays healthy (/health answers), so supervision sees nothing. This is the same failure class as #2097 (design-code-proxy dispatch, no timeout, fallback never fires) in a different proxy whose upstream is a hardcoded public gateway — there is no operator-side workaround short of editing the file.

Acceptance criteria

  • The data-path fetch at :234 carries an explicit deadline (e.g. AbortSignal.timeout(<pinned value>)), and the deadline also bounds reading the response body (cfRes.json()).
  • A deadline expiry is reported as an upstream timeout (a 504-class response naming the timeout), not folded into the generic 500 catch.
  • The pinned value is a named constant (parity with the models fetch's 10000, or a triage-pinned Clef-appropriate value), with the choice recorded on this issue.
  • Tests in bin/clef-proxy.test.mjs cover: a never-responding upstream fails the request at the deadline with the timeout response, and a normally responding upstream is unchanged.
  • The models-catalog fetch behaviour is unchanged.

Related: #2097 (same no-timeout class, design-code-proxy), #2096 / #1809 (this proxy family's env-parsing cases — different failure, same "starts healthy, fails silently" shape).

Activity

  1. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    effort/sSmall: expected under 2 hours of work
    on Oct 29, 2026
  3. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  5. 18 remaining items

  6. added
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    task/fixhas a fix task queued
    and removed
    work/assignedQueue worker lifecycle state
    on Nov 1, 2026
  7. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  8. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    status/triagedTriaged and routed (see agent note)
    on Nov 2, 2026
  9. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up — OpenChamber scheduled lane

  10. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done — scoped data-path timeout PR opened

    • Agent: repo-worker-code (OpenChamber scheduled lane)
    • Task: queue task 5184
    • Outcome: pr_opened; PR fix(clef-proxy): bound data-path gateway requests #4096 fix(clef-proxy): bound data-path gateway requests #4096
    • Change: one 30-second signal bounds gateway fetch and response-body read; deadline expiry maps to 504. Non-timeout upstream handling and the models-catalog timeout remain unchanged.
    • Verification: node --test bin/clef-proxy.test.mjs passed 28/28; git diff --check passed. gh pr checks showed only CodeRabbit skipping; no CI checks were active.
    • Branch/head: queue/5184-2100-data-path-timeout at b3afe605a0a0c6e1a9d31088ca13ef5609b59b29. Queue in-flight labels removed; sandbox backlog/sweep bookkeeping deferred to fleet reconciliation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/clef-proxybin/clef-proxy.mjseffort/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/fixhas a fix task queuedtask/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