Skip to content

agy-proxy: four numeric env vars parsed with bare Number() — 'abc' disables the worker cap and idle eviction and freezes the model catalog, a typo'd turn timeout fires in ~1ms, and MAX_LIVE_WORKERS=0 crashes the eviction loop #2096

Open

Description

@dezren39

Problem

bin/agy-proxy.mjs parses all four of its numeric environment knobs with a bare Number(...) and no validation (main bd880194, file sha256 7de2c21d…):

  • bin/agy-proxy.mjs:168 — MODELS_TTL_MS = Number(process.env.AGY_PROXY_MODELS_TTL_MS || 300000)
  • bin/agy-proxy.mjs:453 — TURN_TIMEOUT_MS = Number(process.env.AGY_PROXY_TURN_TIMEOUT_MS || 300000)
  • bin/agy-proxy.mjs:456 — MAX_LIVE_WORKERS = Number(process.env.AGY_PROXY_MAX_LIVE_WORKERS || 32)
  • bin/agy-proxy.mjs:457 — SESSION_IDLE_TTL_MS = Number(process.env.AGY_PROXY_SESSION_TTL_MS || 30 * 60 * 1000)

The proxy starts healthy with any of these typo'd, and each bad value silently breaks a different safety/behaviour knob instead of failing at startup.

Verified behaviour (laptop probe, verbatim parsing + consumer expressions)

Probe: ~/git/fleet/probe-2026-10-28-laneA-agyenv/probe.mjs on the laptop, against the shipped parsing shape and the exact consumer comparisons. Control (unset / 300) behaves correctly in every case.

  • AGY_PROXY_*='abc' → NaN everywhere:
    • Model catalog never refreshes: :290 Date.now() - modelsFetchedAt > MODELS_TTL_MS is 3600000 > NaN = false even an hour later — the proxy serves the stale/fallback catalog forever.
    • Idle sessions never evict: :695 now - w.lastUsedAt > SESSION_IDLE_TTL_MS is false at any age.
    • The live-worker cap never fires: :701 live.length >= MAX_LIVE_WORKERS is 100 >= NaN = false — warm agy workers accumulate unbounded.
    • Every turn "times out" immediately: :641 setTimeout(..., TURN_TIMEOUT_MS) with NaN is clamped to 1 ms (Node TimeoutNaNWarning), so each turn is killed and reported as agy turn timed out after NaNms.
  • AGY_PROXY_TURN_TIMEOUT_MS='-5' or '9999999999999' (>2³¹): Node clamps both to 1 ms (TimeoutNegativeWarning / TimeoutOverflowWarning) — measured firing at ~3–7 ms vs the 300 ms control firing at 301 ms. Same total breakage as the typo case, with a plausible-looking value in the unit file.
  • AGY_PROXY_MAX_LIVE_WORKERS='0': the eviction loop at :701 runs while (live.length >= 0), which is true even for an empty list; live.shift() yields undefined and destructuring it throws TypeError: undefined is not iterable inside evictLiveWorkersIfNeeded (called from the 60 s interval at :708 and from acquireWorker). Reproduced verbatim in the probe.
  • Negative TTLs invert the gates the other way: MODELS_TTL_MS=-5 re-pulls the catalog on every check, SESSION_IDLE_TTL_MS=-5 evicts every live worker on every sweep.

Why it matters

These are operator knobs in systemd unit environment lines — exactly the place a typo lands. Today a typo does not fail the unit; it disables the resource caps that keep resident warm workers bounded, freezes the model catalog, or makes every request fail as a "timeout" whose message (after NaNms) points at the upstream, not the config. The sibling case in commandcode-proxy is tracked as #1809; this is the same class in a different proxy with four knobs and three distinct failure shapes (never / always / crash), so it needs its own pin — including whether MAX_LIVE_WORKERS=0 is a legitimate "no live workers" setting or an invalid value.

Dedup

Searched open + closed issues for agy-proxy, AGY_PROXY_, and bare Number: #1809 covers only COMMANDCODE_PROXY_TIMEOUT_MS in bin/commandcode-proxy.mjs; #483/#661 covered proxy ports via the strict parseProxyPort helper in bin/proxy-port.mjs. No issue covers agy-proxy's duration/count env vars.

Acceptance criteria

  • All four AGY_PROXY_* numeric env vars are validated at startup through one strict parser (positive finite integer in a stated range; durations also within the 2³¹−1 setTimeout bound): an invalid value fails startup loudly, naming the variable and the offending value — no silent NaN/negative/overflow behaviour.
  • The pinned contract for AGY_PROXY_MAX_LIVE_WORKERS=0 is implemented and tested (either refused as invalid, or honoured without the eviction loop throwing on an empty worker list).
  • Tests in bin/agy-proxy.test.mjs (or a shared helper's tests) cover each var with: unset (default), a valid value, 'abc', a negative, 0 where it matters, and a >2³¹ duration.
  • Valid configurations are unchanged: defaults 300000 / 300000 / 32 / 1800000 and the model-refresh, turn-timeout, idle-eviction, and cap behaviours they drive.

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)
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    app/agy-proxybin/agy-proxy.mjs warm-session proxy
    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. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — design-architect pass 22 (2026-10-30): ADOPTED as Phase 1 of design #2835 (bin/ proxies, with #2823/#2786/#1809): this issue's four knobs parse through the NEW shared helpers Phase 1 adds to bin/proxy-port.mjs — TTLs and turn timeout as millis (digits-only, 1-2^31-1), MAX_LIVE_WORKERS as count (>=1; 0 is a bug spelling, not a setting) — not private guards. Linked as a sub-issue of #2835 (verified in its sub_issues list). Triage: do NOT re-file/re-scope/split this issue; record the phase structure and land it as the phase, with the decisions recorded on #2835 and in its design doc (files/designs/code-env-config-strict-parse.md). This issue keeps its own queued triage packet.

  5. 68 remaining items

  6. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  7. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 4, 2026
  8. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  9. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  10. added
    queue/queuedHas a queued task in the Supabase agent queue
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 5, 2026
  11. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

    • Agent: fleet monitor
    • Session / ID: task 7835 (repo-fleet-monitor run 2026-11-05T15:45:48Z)
    • Found via: monitor reconciliation (Duty 1 claim/label divergence repair)
    • Task: Claim evaporated: task 7835 is queued and unclaimed in repo-health-code-pilot, but this issue still carried claimed labels. Labels reset to the truth (queue/queued + status/needs-triage). Checkpoint stage at evaporation: handed_off.
    • Expectation: The next lane claim picks task 7835 up from the queued state and posts a fresh pickup note.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/agy-proxybin/agy-proxy.mjs warm-session proxyeffort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)queue/parkedQueue task is parked (waiting on CI, time, or an event)queue/queuedHas a queued task in the Supabase agent queuesource/creatorFiled by the new-issue-creator jobsource/design-architectFiled by the repo design architect jobstatus/needs-triageNot yet triaged; needs a first lookstatus/parkedDeliberately paused with a recorded resume conditiontask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions