Skip to content

BA-19: total call-duration deadline (deadlineMs) on the http(s) providers - #28

Merged
hamr0 merged 1 commit into
mainfrom
BA19-fix
Jul 28, 2026
Merged

BA-19: total call-duration deadline (deadlineMs) on the http(s) providers#28
hamr0 merged 1 commit into
mainfrom
BA19-fix

Conversation

@hamr0

@hamr0 hamr0 commented Jul 28, 2026

Copy link
Copy Markdown
Owner

What & why (bareloop upstream ask BA-19)

BA-18's timeoutMs bounds socket inactivity (req.setTimeout), which resets on any socket activity by design — so a zombie stream that trickles a byte forever (bytes arriving, the response never completing) never trips it. An adopter saw one generate() run 274 minutes and end in ECONNRESET (not a TimeoutError): the reset proves bytes were flowing, so the idle timer never fired. Nothing bounded total call duration.

The fix

Opt-in deadlineMs on all four http(s) providers — an absolute, non-resetting wall-clock ceiling (applyRequestDeadline in the shared src/provider-http.js).

  • Disabled by default (a deliberately long single call is legitimate; a default would kill it); 0/Infinity disable; per-call overridable.
  • On trip: a terminal TimeoutError distinguishable from the idle trip — code:'EDEADLINE', context.bound:'deadline', retryable:false (a hard ceiling is meant to STOP, not re-spend). The idle trip now also carries context.bound:'idle'.
  • With both armed and timeoutMs < deadlineMs, a silent socket trips the idle bound first.

Review fixes folded in

  • Finding 1 (correctness): a garbage/non-numeric deadlineMs no longer silently disables the deadline (which would reintroduce the hang) — resolveTimeoutMs throws a ValidationError on an explicitly-set garbage value when the knob has no safe default; the idle bound still fails safe to its 10-min default (BA-18 unchanged).
  • Finding 2 (cleanup): idle + deadline wiring collapsed to one applyRequestBounds seam per _request, so a future third bound is added in one place.

Verification

  • POC 5/5; +23 tests across all four providers (all four acceptance criteria + review-fix edges).
  • Mutations proven RED incl. a deadline-OFF negative control that hangs, and both review-fix mutations.
  • Full suite 1042 pass / 0 fail / 2 skip; typecheck + build:types exit 0.

🤖 Generated with Claude Code

…s) providers (a zombie stream was a multi-hour hang the idle timeout could not catch)

BA-18's timeoutMs bounds socket INACTIVITY (req.setTimeout), which resets on any
socket activity by design — so a "zombie stream" that trickles a byte forever
(bytes arriving, the response never completing) never trips it. An adopter saw one
generate() run 274 min and end in ECONNRESET, not a TimeoutError: the reset proves
bytes were flowing, so the idle timer never fired. Nothing bounded total call
duration.

Add an opt-in deadlineMs on all four http(s) providers — an absolute, non-resetting
wall-clock ceiling on the whole request (applyRequestDeadline in the shared
src/provider-http.js). DISABLED by default (a deliberately long single call is
legitimate; a default would kill it); 0/Infinity disable; per-call overridable.

On trip: a TERMINAL TimeoutError distinguishable from the idle trip — code
'EDEADLINE', context.bound 'deadline', retryable:false (a hard ceiling is meant to
STOP, not re-spend a full deadlineMs on retry). The idle trip now also carries
context.bound 'idle' so a consumer switches on one uniform field. With both armed
and timeoutMs < deadlineMs, a silent socket trips the idle bound first.

Review fixes folded in:
- Finding 1 (correctness): a garbage/non-numeric deadlineMs no longer silently
  disables the deadline (which would reintroduce the hang). resolveTimeoutMs throws
  a ValidationError on an explicitly-set garbage value when the knob has no safe
  default; the idle bound still fails safe to its 10-min default (BA-18 unchanged).
- Finding 2 (cleanup): idle + deadline wiring collapsed to one applyRequestBounds
  seam per _request, so a future third bound is added in one place, not four.

POC 5/5; +23 tests across all four providers (all four acceptance criteria + the
review-fix edges); mutations proven RED incl. a deadline-OFF negative control that
hangs and both review-fix mutations; full suite 1042 pass / 0 fail / 2 skip;
typecheck clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6D6DPGiVG1Jo3vMbwqFPV
@hamr0
hamr0 merged commit 0037182 into main Jul 28, 2026
2 checks passed
@hamr0
hamr0 deleted the BA19-fix branch July 28, 2026 01:10
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