{"record":{"id":"aa17112b0a2fbacb","repo":"can1357/oh-my-pi","slug":"failed-to-load-tree-sitter-language-err","errorCode":null,"errorMessage":"Failed to load tree-sitter language: {err}","messagePattern":"Failed to load tree-sitter language: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/pi-ast/src/parse_cache.rs","lineNumber":217,"sourceCode":"/// Semantics match a bare `Parser::new()` / `set_language` / `parse` sequence\n/// exactly: `Err` when the grammar fails to load, `Ok(None)` when `parse`\n/// yields nothing, `Ok(Some(tree))` otherwise. Trees carrying syntax errors are\n/// cached like any other — `has_error()` is a property of the tree, so callers\n/// that reject on it reach the identical verdict from a cached tree, and\n/// repeated \"does this parse\" probes over the same broken file get the speedup\n/// too.\npub fn parse_cached(code: &str, lang: SupportLang) -> Result<Option<Tree>> {\n\tlet key = key_for(code, lang);\n\t// Bound the guard to a `let` so it drops at the end of this statement: an\n\t// `if let` scrutinee would hold the lock across the early return.\n\tlet cached = lock().get(&key, code);\n\tif let Some(tree) = cached {\n\t\treturn Ok(Some(tree));\n\t}\n\tlet mut parser = Parser::new();\n\tparser\n\t\t.set_language(&lang.get_ts_language())\n\t\t.map_err(|err| anyhow!(\"Failed to load tree-sitter language: {err}\"))?;\n\tlet Some(tree) = parser.parse(code, None) else {\n\t\treturn Ok(None);\n\t};\n\tlock().insert(key, code, &tree);\n\tOk(Some(tree))\n}\n\n/// Occupancy and counters, for diagnostics.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]\npub struct ParseCacheStats {\n\tpub entries:      usize,\n\tpub source_bytes: usize,\n\tpub hits:         u64,\n\tpub misses:       u64,\n\tpub evictions:    u64,\n}\n\npub fn parse_cache_stats() -> ParseCacheStats {","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-ast/src/parse_cache.rs#L199-L235","documentation":"_validate_history_limit guards configuration values for history limits: None is allowed (meaning unset/default), but any non-None limit <= 0 is rejected with ValueError, since a zero or negative history cap is meaningless.","triggerScenarios":"Passing history_limit=0 or a negative number to RpcClient configuration (constructor option, setter, or config file parsed into an int) — e.g. MAX_HISTORY=0 in environment-derived config.","commonSituations":"Env vars parsed with int() where the user set 0 or -1 to mean 'unlimited' (should be None/omitted instead), templated configs defaulting to 0, off-by-one sign errors computing a limit.","solutions":["Pass None (or omit the option) to use the default/unlimited history, not 0.","Fix env/config parsing: map sentinel values like 0 or -1 to None before constructing the client.","Clamp computed limits: max(1, computed_limit).","Catch ValueError around client construction to report a clear configuration error."],"exampleFix":"// before\nraw = os.environ.get(\"OMP_HISTORY_LIMIT\", \"0\")\nclient = RpcClient(cmd, history_limit=int(raw))  # ValueError: must be > 0\n// after\nraw = os.environ.get(\"OMP_HISTORY_LIMIT\")\nhistory_limit = int(raw) if raw and int(raw) > 0 else None\nclient = RpcClient(cmd, history_limit=history_limit)","handlingStrategy":"validation","validationCode":"def clean_limit(v):\n    if v is None: return None\n    if not isinstance(v, int) or v <= 0:\n        raise ValueError(f\"history limit must be a positive int or None, got {v!r}\")\n    return v\nhistory_limit = clean_limit(cfg.get(\"history_limit\"))","typeGuard":"def is_valid_limit(v: object) -> TypeGuard[Optional[int]]:\n    return v is None or (isinstance(v, int) and not isinstance(v, bool) and v > 0)","tryCatchPattern":"try:\n    client = RpcClient(cmd, history_limit=limit)\nexcept ValueError as exc:\n    if \"must be greater than zero\" in str(exc):\n        raise ValueError(f\"bad history_limit config: {limit!r}\") from exc\n    raise","preventionTips":["Use None (or omit) for unlimited/default — never 0 or -1.","Sanitize env-derived config: map sentinel values to None before constructing the client.","Validate config once at load time so zero/negative limits never reach client construction."],"tags":["validation","config","valueerror","history"],"backgroundTag":"invalid-configuration-value","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}