{"record":{"id":"bc44a9d55182703a","repo":"headroomlabs-ai/headroom","slug":"recommendations-toml-parse-error-0","errorCode":null,"errorMessage":"recommendations TOML parse error: {0}","messagePattern":"recommendations TOML parse error: (.+?)","errorType":"exception","errorClass":"RecommendationsError","httpStatus":null,"severity":"warning","filePath":"crates/headroom-core/src/transforms/recommendations.rs","lineNumber":262,"sourceCode":"}\n\n/// Errors surfaced by the loader. Marked non-exhaustive so we can add\n/// future variants without breaking callers.\n#[derive(Debug, thiserror::Error)]\n#[non_exhaustive]\npub enum RecommendationsError {\n    /// File doesn't exist on disk.\n    #[error(\"recommendations file not found: {0}\")]\n    Missing(PathBuf),\n    /// Filesystem error other than NotFound.\n    #[error(\"recommendations IO error at {path}: {source}\")]\n    Io {\n        path: PathBuf,\n        #[source]\n        source: std::io::Error,\n    },\n    /// TOML parse failure (typed wrapper for ergonomics).\n    #[error(\"recommendations TOML parse error: {0}\")]\n    Parse(#[from] toml::de::Error),\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn sample_toml() -> &'static str {\n        r#\"\n[[recommendation]]\nauth_mode = \"payg\"\nmodel_family = \"claude-3-5\"\nstructure_hash = \"deadbeef\"\nskip_compression_recommended = true\nstrategy_hint = \"smart_crusher\"\nconfidence = 0.87\nobservations = 142\n","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/crates/headroom-core/src/transforms/recommendations.rs#L244-L280","documentation":"An HTTP 404 deliberately returned by the require_loopback FastAPI dependency (loopback_guard.py:203) when the TCP peer is not a loopback address. Debug/admin endpoints (/debug/*, /admin/*, /stats/reset, /v1/telemetry, etc.) are local-only, and 404 rather than 403 is chosen so external scanners cannot distinguish the routes from nonexistent ones.","triggerScenarios":"Curling http://<lan-ip>:<port>/debug/tasks from another machine, or from a container where the app is reached via a non-loopback docker network address; a reverse proxy forwarding with the original client IP preserved so request.client.host is the remote address.","commonSituations":"Accessing the admin dashboard through a docker port mapping from another host; putting the proxy behind an external ingress and expecting the debug endpoints to be reachable; health checks from a different pod.","solutions":["Make the request from the same host via 127.0.0.1/localhost (e.g. docker exec into the container and curl 127.0.0.1)","If remote access is genuinely required, front the endpoint with your own authenticated reverse proxy bound to loopback, or tunnel (ssh -L) to the host","Verify request.client.host is actually loopback — proxies using unix sockets or forwarded connections may present a non-loopback peer"],"exampleFix":"# before: from another machine\ncurl http://192.168.1.20:8080/debug/tasks\n\n# after: on the host itself\ncurl http://127.0.0.1:8080/debug/tasks","handlingStrategy":"validation","validationCode":"import ipaddress, socket\ndef is_loopback_peer() -> bool:\n    try:\n        return ipaddress.ip_address(socket.gethostbyname(socket.gethostname())).is_loopback\n    except OSError:\n        return False\n# only target /debug,/admin,/v1/telemetry routes when running on the same host","typeGuard":null,"tryCatchPattern":"if response.status_code == 404 and route_exists_in_docs:\n    # guard rejection — run from the host instead\n    ...","preventionTips":["Run admin/debug curls from the same host or via ssh -L tunnel","Remember 404 here is intentional camouflage, not a missing route"],"tags":["security","loopback","http-404","admin-endpoints"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}