Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/claude_agent_sdk/_internal/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import json
import logging
import math
import os
import uuid
from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable, Mapping
Expand Down Expand Up @@ -65,9 +66,9 @@ def run_end_ceiling_ms(options_env: Mapping[str, str]) -> int:

``options_env`` (``ClaudeAgentOptions.env``) overrides the inherited
environment, as it does for the CLI subprocess. ``0`` means no limit;
anything that is not a plain non-negative integer falls back to the CLI's
default of 10 minutes (the CLI itself also reads spellings such as
``1e6``).
anything that is not a non-negative number of milliseconds falls back to
the CLI's default of 10 minutes. Scientific notation is read the way the
CLI reads it, so ``1e6`` is a million milliseconds and not a fallback.
"""
if _RUN_END_CEILING_ENV in options_env:
raw: Any = options_env[_RUN_END_CEILING_ENV]
Expand All @@ -78,7 +79,16 @@ def run_end_ceiling_ms(options_env: Mapping[str, str]) -> int:
try:
value = int(raw)
except (TypeError, ValueError):
return DEFAULT_RUN_END_CEILING_MS
# Only reached when the plain-integer read fails, so an integer too
# large for a float keeps its exact value above and is clamped later
# by the sleeper rather than becoming an infinity here.
try:
as_number = float(raw)
except (TypeError, ValueError):
return DEFAULT_RUN_END_CEILING_MS
if not math.isfinite(as_number):
return DEFAULT_RUN_END_CEILING_MS
value = int(as_number)
return value if value >= 0 else DEFAULT_RUN_END_CEILING_MS


Expand Down
12 changes: 12 additions & 0 deletions tests/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -1357,6 +1357,12 @@ class TestRunEndCeilingFromEnv:
({"CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS": "soon"}, None, 600_000),
({"CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS": "-1"}, None, 600_000),
({"CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS": ""}, "1234", 600_000),
({"CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS": "1e6"}, None, 1_000_000),
({"CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS": "1e2"}, None, 100),
({"CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS": "-1e3"}, None, 600_000),
({"CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS": "inf"}, None, 600_000),
({"CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS": "nan"}, None, 600_000),
({"CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS": "9" * 400}, None, int("9" * 400)),
],
ids=[
"default",
Expand All @@ -1367,6 +1373,12 @@ class TestRunEndCeilingFromEnv:
"not_a_number",
"negative",
"empty_option_wins",
"scientific",
"scientific_small",
"scientific_negative",
"infinity",
"not_a_number_float",
"beyond_float_range",
],
)
def test_parse(self, monkeypatch, options_env, ambient, expected):
Expand Down