helix-editor/helix · warning · anyhow::Error
Unknown key `{}`
Error message
Unknown key `{}` What it means
`:get <key>` serializes the live editor config to JSON and resolves the (lowercased) dotted key as a JSON pointer ('/a/b'). Any path that does not exist in the serialized config — wrong name, wrong path segment, or a key from a different helix version — returns 'Unknown key `x`'. No state is changed; it is a read-only lookup.
Source
Thrown at helix-term/src/commands/typed.rs:2267
// When a user hits backspace and there are no numbers left,
// we can bring them back to their original selection. If they
// begin typing numbers again, we'll start a new preview session.
PromptEvent::Update if args.is_empty() => abort_goto_line_number_preview(cx),
PromptEvent::Update => update_goto_line_number_preview(cx, args)?,
}
Ok(())
}
// Fetch the current value of a config option and output as status.
fn get_option(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> {
if event != PromptEvent::Validate {
return Ok(());
}
let key = &args[0].to_lowercase();
let key_error = || anyhow::anyhow!("Unknown key `{}`", key);
let config = serde_json::json!(cx.editor.config().deref());
let pointer = format!("/{}", key.replace('.', "/"));
let value = config.pointer(&pointer).ok_or_else(key_error)?;
cx.editor.set_status(value.to_string());
Ok(())
}
/// Change config at runtime. Access nested values by dot syntax, for
/// example to disable smart case search, use `:set search.smart-case false`.
fn set_option(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> anyhow::Result<()> {
if event != PromptEvent::Validate {
return Ok(());
}
let (key, arg) = (&args[0].to_lowercase(), args[1].trim());
View on GitHub (pinned to 079a789e8c)
Solutions
- Copy the exact dotted path from the config documentation — the same names config.toml uses (e.g. `:get search.smart-case`).
- Query parent keys stepwise (`:get search`) to discover the existing subtree.
- Use `:config-open` to see a config with valid keys side by side.
Example fix
# before :get smartcase # after :get search.smart-case
Defensive patterns
Strategy: validation
Validate before calling
// same resolution :get uses — probe the pointer before reading
let cfg = serde_json::json!(editor.config().deref());
let pointer = format!("/{}", key.to_lowercase().replace('.', "/"));
if cfg.pointer(&pointer).is_none() { /* unknown key: reject early */ } Type guard
fn config_key_exists(editor: &Editor, key: &str) -> bool {
let cfg = serde_json::json!(editor.config().deref());
cfg.pointer(&format!("/{}", key.to_lowercase().replace('.', "/"))).is_some()
} Prevention
- Discover keys with `:get <parent>` before scripting reads of leaf keys.
- Re-verify dotted paths after helix upgrades; config keys move between versions.
When it happens
Trigger: `:get bogus`, `:get lsp.bogus`, or a key valid in another helix version; also wrong path segment order like `:get smart-case.search`.
Common situations: Guessing key names instead of copying from the config reference; version drift after an upgrade renamed/moved keys; typos.
Related errors
- Command not provided
- Incorrect transport {}
- Language server '{name}' not defined
- Failed to load config: {}
- --config must specify a path to read
AI-assisted analysis of helix-editor/helix@079a789e8c (2026-08-16).
Data as JSON: /api/errors/fd19e2eef2e1405a.
Report an issue: GitHub.