Skip to content

SEC_FETCH_MAX_PER_SEC=0 resolves to 8 — the documented maximum — against a rate limit this repo documents as self-sustaining #368

Description

@sroussey

src/config/Constants.ts:27-31:

export const SecFetchMaxPerSec = ((): number => {
  const raw = process.env.SEC_FETCH_MAX_PER_SEC?.trim();
  const parsed = raw !== undefined && /^\d+$/.test(raw) ? Number(raw) : 4;
  return parsed >= 1 && parsed <= 8 ? parsed : 8;
})();

Anything in range passes; anything out of range becomes the ceiling. For a value above 8 that
is a correct clamp down. For 0 it is an inversion: the one value an operator types meaning
less is the one value that produces more.

Measured

$ for v in "" 0 1 4 8 9 100 abc -1; do
    SEC_FETCH_MAX_PER_SEC="$v" bun -e 'import {SecFetchMaxPerSec, SecFetchMaxConcurrent, SecSqliteCacheMb}
      from "./src/config/Constants.ts";
      console.log(`perSec=${SecFetchMaxPerSec} concurrent=${SecFetchMaxConcurrent} cacheMb=${SecSqliteCacheMb}`)'
  done

SEC_FETCH_MAX_PER_SEC=''    -> perSec=4  concurrent=4 cacheMb=256
SEC_FETCH_MAX_PER_SEC='0'   -> perSec=8  concurrent=4 cacheMb=256      <-- 2× the default
SEC_FETCH_MAX_PER_SEC='1'   -> perSec=1
SEC_FETCH_MAX_PER_SEC='4'   -> perSec=4
SEC_FETCH_MAX_PER_SEC='8'   -> perSec=8
SEC_FETCH_MAX_PER_SEC='9'   -> perSec=8                                 (correct clamp down)
SEC_FETCH_MAX_PER_SEC='100' -> perSec=8                                 (correct clamp down)
SEC_FETCH_MAX_PER_SEC='abc' -> perSec=4                                 (falls back)
SEC_FETCH_MAX_PER_SEC='-1'  -> perSec=4                                 (falls back)

Why it matters here specifically

The JSDoc four lines above the code says "Override DOWN via SEC_FETCH_MAX_PER_SEC (1–8); the
ceiling is clamped to 8 so we stay consistently under EDGAR's limit and a stray higher value can't
push us to the edge."
A stray lower value pushes us to the edge instead.

And docs/fetch-and-storage.md records that EDGAR's rate-limit block is self-sustaining
requests sent during a time-out extend it — which is why the default is held at 4 rather than at
EDGAR's documented 10. Doubling the rate is the failure mode the whole 451a0923 arc was built to
avoid.

The two neighbours already do it right

Same file, same shape, different outcome for 0:

// :62-66
return Math.min(64, Math.max(1, parsed || 4));      // SecFetchMaxConcurrent: 0 -> 4
// :96-100
return Math.min(4096, Math.max(2, parsed || 256));  // SecSqliteCacheMb:      0 -> 256

Fix

Either match the neighbours (Math.min(8, Math.max(1, parsed || 4))) or — better, and the policy
#323 has been asking for since 2026-08-24 — throw on a value outside 1–8, naming the variable
and the range, the way secEmbeddingDimensions() now throws on a malformed
SEC_EMBEDDING_DIMENSIONS (src/config/models.ts:73-80). That throw landed this window and is
the right precedent; this is the knob where silence costs the most.

Filed separately from #323 because it is not a swallowed typo — the value is read, accepted, and
inverted — and because it is the one knob whose failure mode is an IP block rather than a
suboptimal default.

Found during the 2026-09-14 review. Snapshot: workglow-dev/prdanalysis/grades/2026-09-14/sec-detailed.md §4.4.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions