can1357/oh-my-pi · error · anyhow::Error

Failed to load tree-sitter language: {err}

Error message

Failed to load tree-sitter language: {err}

What it means

_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.

Source

Thrown at crates/pi-ast/src/parse_cache.rs:217

/// Semantics match a bare `Parser::new()` / `set_language` / `parse` sequence
/// exactly: `Err` when the grammar fails to load, `Ok(None)` when `parse`
/// yields nothing, `Ok(Some(tree))` otherwise. Trees carrying syntax errors are
/// cached like any other — `has_error()` is a property of the tree, so callers
/// that reject on it reach the identical verdict from a cached tree, and
/// repeated "does this parse" probes over the same broken file get the speedup
/// too.
pub fn parse_cached(code: &str, lang: SupportLang) -> Result<Option<Tree>> {
	let key = key_for(code, lang);
	// Bound the guard to a `let` so it drops at the end of this statement: an
	// `if let` scrutinee would hold the lock across the early return.
	let cached = lock().get(&key, code);
	if let Some(tree) = cached {
		return Ok(Some(tree));
	}
	let mut parser = Parser::new();
	parser
		.set_language(&lang.get_ts_language())
		.map_err(|err| anyhow!("Failed to load tree-sitter language: {err}"))?;
	let Some(tree) = parser.parse(code, None) else {
		return Ok(None);
	};
	lock().insert(key, code, &tree);
	Ok(Some(tree))
}

/// Occupancy and counters, for diagnostics.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ParseCacheStats {
	pub entries:      usize,
	pub source_bytes: usize,
	pub hits:         u64,
	pub misses:       u64,
	pub evictions:    u64,
}

pub fn parse_cache_stats() -> ParseCacheStats {

View on GitHub (pinned to 9690622007)

Solutions

  1. Pass None (or omit the option) to use the default/unlimited history, not 0.
  2. Fix env/config parsing: map sentinel values like 0 or -1 to None before constructing the client.
  3. Clamp computed limits: max(1, computed_limit).
  4. Catch ValueError around client construction to report a clear configuration error.

Example fix

// before
raw = os.environ.get("OMP_HISTORY_LIMIT", "0")
client = RpcClient(cmd, history_limit=int(raw))  # ValueError: must be > 0
// after
raw = os.environ.get("OMP_HISTORY_LIMIT")
history_limit = int(raw) if raw and int(raw) > 0 else None
client = RpcClient(cmd, history_limit=history_limit)
Defensive patterns

Strategy: validation

Validate before calling

def clean_limit(v):
    if v is None: return None
    if not isinstance(v, int) or v <= 0:
        raise ValueError(f"history limit must be a positive int or None, got {v!r}")
    return v
history_limit = clean_limit(cfg.get("history_limit"))

Type guard

def is_valid_limit(v: object) -> TypeGuard[Optional[int]]:
    return v is None or (isinstance(v, int) and not isinstance(v, bool) and v > 0)

Try / catch

try:
    client = RpcClient(cmd, history_limit=limit)
except ValueError as exc:
    if "must be greater than zero" in str(exc):
        raise ValueError(f"bad history_limit config: {limit!r}") from exc
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid configuration value" and "Unsupported/Unknown setting value" errors: why libraries reject your config strings, numbers, and types — this error's family across 30 libraries.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/aa17112b0a2fbacb. Report an issue: GitHub.