{"record":{"id":"2a4ef53ff97d5b45","repo":"langchain-ai/deepagents","slug":"name-must-be-a-positive-finite-number-got-budg","errorCode":null,"errorMessage":"{name} must be a positive finite number, got {budget!r}","messagePattern":"(.+?) must be a positive finite number, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/auto_mode.py","lineNumber":2184,"sourceCode":"        ):\n            msg = \"trusted_compaction_tool must be named compact_conversation\"\n            raise ValueError(msg)\n        # The review deadline is a security control's budget, so reject a\n        # nonsensical one at the boundary rather than trusting every caller:\n        # a zero, negative, or NaN timeout expires immediately, silently turning\n        # Auto into \"deny every gated batch, then escalate\". Callers that read\n        # user config go through `resolve_auto_classifier_timeout`, which bounds\n        # the value; this guards programmatic construction.\n        for name, budget in (\n            (\"classifier_timeout_seconds\", classifier_timeout_seconds),\n            (\n                \"classifier_construction_timeout_seconds\",\n                classifier_construction_timeout_seconds,\n            ),\n        ):\n            if not math.isfinite(budget) or budget <= 0:\n                msg = f\"{name} must be a positive finite number, got {budget!r}\"\n                raise ValueError(msg)\n        interrupt_map = dict(interrupt_on)\n        interrupt_map[\"create_temp_artifact\"] = {\n            \"allowed_decisions\": [\"approve\", \"reject\"],\n            \"description\": \"Create an exclusively allocated OS-temp scratch file.\",\n        }\n        interrupt_map[\"delete_temp_artifact\"] = {\n            \"allowed_decisions\": [\"approve\", \"reject\"],\n            \"description\": \"Delete an exact current-request OS-temp scratch file.\",\n        }\n        super().__init__(interrupt_map)\n        self._worktree_root = Path(worktree_root).resolve(strict=False)\n        from deepagents_code._git import read_git_remote_url_from_filesystem\n\n        origin = read_git_remote_url_from_filesystem(self._worktree_root) or \"\"\n        self._trusted_environment = {\n            \"worktree_root\": str(self._worktree_root),\n            \"origin_remote\": _redact_remote(origin),\n        }","sourceCodeStart":2166,"sourceCodeEnd":2202,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/auto_mode.py#L2166-L2202","documentation":"`AutoModeHITLMiddleware.__init__` validates the review-deadline budgets (e.g. `classifier_construction_timeout_seconds`) as a security control: a zero, negative, NaN, or infinite timeout would expire immediately and silently turn Auto mode into 'deny every gated batch, then escalate'. Any budget value that is not a positive finite number raises this ValueError at the boundary.","triggerScenarios":"Passing `classifier_construction_timeout_seconds=0`, a negative number, `float('nan')`, or `float('inf')` (or the same for other budget parameters in the checked list) to `AutoModeHITLMiddleware.__init__`.","commonSituations":"Loading a timeout from config where 0 means 'no timeout' in another library; YAML/JSON config containing null/NaN; computing a deadline from a clock skew or subtraction that yields a non-positive value; programmatic construction bypassing `resolve_auto_classifier_timeout`, which normally bounds the value.","solutions":["Route user config through `resolve_auto_classifier_timeout`, which clamps the value to a valid positive bound.","Pass a positive finite float/int, e.g. 30 (seconds), for every budget parameter.","Sanitize loaded config: reject or replace 0/negative/NaN/inf values before constructing the middleware."],"exampleFix":"// before\nmw = AutoModeHITLMiddleware(classifier_construction_timeout_seconds=0)  # config default meaning 'unset'\n// after\nbudget = resolve_auto_classifier_timeout(config.get('classifier_timeout'))  # bounds the value\nmw = AutoModeHITLMiddleware(classifier_construction_timeout_seconds=budget)","handlingStrategy":"validation","validationCode":"import math\nfor name, budget in [('classifier_construction_timeout_seconds', cfg.get('classifier_construction_timeout_seconds'))]:\n    if budget is not None and (not math.isfinite(budget) or budget <= 0):\n        raise ValueError(f'{name} invalid: {budget!r}')","typeGuard":"def is_valid_budget(value: object) -> bool:\n    return isinstance(value, (int, float)) and not isinstance(value, bool) and math.isfinite(value) and value > 0","tryCatchPattern":"try:\n    mw = AutoModeHITLMiddleware(classifier_construction_timeout_seconds=budget)\nexcept ValueError as e:\n    if 'must be a positive finite number' in str(e):\n        budget = resolve_auto_classifier_timeout(None)  # fall back to bounded default\n        mw = AutoModeHITLMiddleware(classifier_construction_timeout_seconds=budget)\n    else:\n        raise","preventionTips":["Always construct budgets via `resolve_auto_classifier_timeout` instead of passing raw config values.","Reject 0/negative/NaN/inf at config-load time with a clear error of your own.","Remember that 0 usually means 'unset' elsewhere but is invalid here — map unset to None or a positive default.","Add a unit test that feeds NaN and 0 to config parsing."],"tags":["validation","timeout","configuration","auto-mode"],"backgroundTag":"invalid-timeout-value","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}