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

Unable to infer language from file extension: {}. Specify `l

Error message

Unable to infer language from file extension: {}. Specify `lang` explicitly.

What it means

When a set_todos() seed is a dict (phase seed), the client requires a 'name' key holding a non-empty, non-whitespace string. A missing, empty, or whitespace-only name means the phase cannot be identified or displayed, so RpcError is raised during normalization before sending.

Source

Thrown at crates/pi-ast/src/ops.rs:82

}

#[must_use]
pub fn supported_lang_list() -> String {
	SupportLang::sorted_aliases().join(", ")
}

pub fn resolve_supported_lang(value: &str) -> Result<SupportLang> {
	SupportLang::from_alias(value).ok_or_else(|| {
		anyhow!("Unsupported language '{value}'. Supported: {}", supported_lang_list())
	})
}

pub fn resolve_language(lang: Option<&str>, file_path: &Path) -> Result<SupportLang> {
	if let Some(lang) = lang.map(str::trim).filter(|lang| !lang.is_empty()) {
		return resolve_supported_lang(lang);
	}
	SupportLang::from_path(file_path).ok_or_else(|| {
		anyhow!(
			"Unable to infer language from file extension: {}. Specify `lang` explicitly.",
			file_path.display()
		)
	})
}

#[must_use]
pub fn is_supported_file(file_path: &Path, explicit_lang: Option<&str>) -> bool {
	if explicit_lang.is_some() {
		return true;
	}
	resolve_language(None, file_path).is_ok()
}

pub fn compile_pattern(
	pattern: &str,
	selector: Option<&str>,
	strictness: &MatchStrictness,

View on GitHub (pinned to 9690622007)

Solutions

  1. Add a non-empty 'name' string to every phase seed dict.
  2. Strip/validate names before calling set_todos(); drop or rename phases with empty names.
  3. If you meant flat todo items, pass plain item dicts instead of phase-shaped dicts (keys like 'name' make it look like a phase).
  4. Catch RpcError during normalization and report which phase index lacks a name.

Example fix

// before
client.set_todos([{"id": "p1", "tasks": [{"title": "do it"}]}])
// after
client.set_todos([{"id": "p1", "name": "Setup", "tasks": [{"title": "do it"}]}])
Defensive patterns

Strategy: validation

Validate before calling

for i, seed in enumerate(phases):
    name = seed.get("name") if isinstance(seed, dict) else None
    if not isinstance(name, str) or not name.strip():
        raise ValueError(f"phase {i + 1} needs a non-empty 'name'")

Type guard

def is_valid_phase_seed(seed: object) -> TypeGuard[dict]:
    name = seed.get("name") if isinstance(seed, dict) else None
    return isinstance(name, str) and bool(name.strip())

Try / catch

try:
    client.set_todos(seeds)
except RpcError as exc:
    if "non-empty 'name'" in str(exc):
        raise ValueError(f"invalid phase in set_todos: {exc}") from exc
    raise

Prevention

When it happens

Trigger: Passing a phase dict like {"tasks": [...]} with no 'name', or {"name": ""} or {"name": " "} to set_todos().

Common situations: Programmatically building phases where name was conditionally omitted, deserializing phases from JSON/DB where an empty name was stored, or confusing flat todo items (which don't need name) with phase seeds (which do).

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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