{"record":{"id":"1023813d3cb83f92","repo":"headroomlabs-ai/headroom","slug":"native-detect-content-type-exceeded-timeout-1f-s","errorCode":null,"errorMessage":"native detect_content_type exceeded {timeout:.1f}s watchdog","messagePattern":"native detect_content_type exceeded (.+?)s watchdog","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"warning","filePath":"headroom/transforms/content_router.py","lineNumber":863,"sourceCode":"    compression-executor worker — see #575's executor-saturation report).\n\n    # ponytail: can't kill a GIL-released native call; the watchdog frees the\n    # caller and the stuck daemon thread is left to die with the process. The\n    # upgrade path is the Rust-side fix that makes first-call init non-blocking.\n    \"\"\"\n    box: dict[str, Any] = {}\n\n    def _run() -> None:\n        try:\n            box[\"result\"] = rust_detect(content)\n        except BaseException as exc:  # noqa: BLE001 — relayed to the caller's degrade path\n            box[\"error\"] = exc\n\n    worker = threading.Thread(target=_run, name=\"headroom-detect-watchdog\", daemon=True)\n    worker.start()\n    worker.join(timeout)\n    if worker.is_alive():\n        raise TimeoutError(f\"native detect_content_type exceeded {timeout:.1f}s watchdog\")\n    if \"error\" in box:\n        raise box[\"error\"]\n    return box[\"result\"]\n\n\n# Coding agents commonly wrap each tool result in an envelope such as\n# ``<returncode>0</returncode>\\n<output>...</output>`` (or <stdout>/<stderr>/\n# <tool_result>). Those wrapper tags make the native detector read the whole\n# payload as markup (HTML/XML) even though the inner content is source code, a\n# grep result, or a log. That misroutes to the HTML article-extractor, which\n# blanks or corrupts code (dropping identifiers and route converters). Detect on\n# the inner payload so the real content type wins; compression still runs on the\n# original content.\n_DETECTION_ENVELOPE_RE = re.compile(\n    r\"\\A\\s*(?:<returncode>\\s*-?\\d+\\s*</returncode>\\s*)?\"\n    r\"<(?P<tag>output|stdout|stderr|tool_result|result)>\\n?\"\n    r\"(?P<body>.*?)\"\n    r\"\\n?</(?P=tag)>\\s*\\Z\",","sourceCodeStart":845,"sourceCodeEnd":881,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/transforms/content_router.py#L845-L881","documentation":"Raised by the watchdog wrapper around the native (Rust/PyO3) detect_content_type in content_router.py: a daemon thread runs rust_detect(content) and if it is still alive after the timeout, the call is declared hung and TimeoutError is raised — you cannot interrupt a stuck native call from Python, so the thread is abandoned and the caller degrades instead of blocking forever. Any exception the thread did raise is re-raised verbatim afterwards.","triggerScenarios":"Calling the native content detection path with input that makes the Rust detector loop or take longer than the watchdog timeout — e.g. pathological regex-ish payloads, huge markup-nested tool envelopes, or adversarial content.","commonSituations":"Compressing very large or deeply nested tool outputs through the content router; a Rust detector regression on a new input shape; slow/loaded machines pushing a normally-fast parse past the timeout.","solutions":["Catch TimeoutError and fall back to the pure-Python detector or treat content as plain text (this is the designed degrade path).","If it reproduces, minimize the input and report it — a detector exceeding the watchdog indicates a native bug worth filing.","Raise the watchdog timeout only if profiling shows legitimate large inputs, not a hang."],"exampleFix":"# before\nctype = detect_content_type_wrapped(content)  # may hang -> TimeoutError\n\n# after\ntry:\n    ctype = detect_content_type_wrapped(content)\nexcept TimeoutError:\n    ctype = \"text/plain\"  # designed degrade path","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    ctype = detect_wrapped(content)\nexcept TimeoutError:\n    logger.warning(\"native detect timed out; degrading to text/plain\")\n    ctype = \"text/plain\"","preventionTips":["Always keep a non-native fallback wired for content detection.","Log the payload size when the watchdog fires — oversized inputs may need pre-truncation.","Report reproducible hangs upstream; the watchdog is a safety net, not a fix."],"tags":["timeout","watchdog","native","rust","content-detection"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}