rtk-ai/rtk · error
ambiguous hash prefix
Error message
ambiguous hash prefix '{hash}': matches {} What it means
RTK's recall store lets you look up a recorded command output by a hash prefix instead of the full hash. `load_by_hash` queries all rows whose hash starts with the prefix; if more than one row matches, the prefix is ambiguous and the lookup is deliberately refused so you never silently fetch the wrong output. The error lists every matching full hash so you can disambiguate by using a longer prefix or one of the exact hashes.
Solutions
- Re-run the recall with a longer hash prefix until it uniquely matches one entry.
- Use one of the full hashes printed in the error message to fetch the exact record.
- List the recall table (e.g. via rtk gain/history or a direct sqlite query of the recall table) to find the correct full hash before recalling.
Example fix
// before rtk recall ab1 // error: ambiguous hash prefix 'ab1': matches ab12..., ab9f... // after rtk recall ab1234 # longer prefix, or the full hash
Defensive patterns
Strategy: validation
Validate before calling
// Check prefix uniqueness before recalling
let matches: Vec<String> = list_recall_hashes()
.await?
.into_iter()
.filter(|h| h.starts_with(&prefix))
.collect();
if matches.len() > 1 {
eprintln!("ambiguous prefix {prefix}: use one of {:?}", matches);
return Ok(());
} Type guard
fn unique_prefix(prefix: &str, hashes: &[String]) -> Option<String> {
let m: Vec<_> = hashes.iter().filter(|h| h.starts_with(prefix)).collect();
match m.as_slice() {
[one] => Some((*one).clone()),
_ => None,
}
} Try / catch
match rtk_recall(&prefix) {
Err(e) if e.to_string().contains("ambiguous hash prefix") => {
// retry with a longer prefix or pick a full hash from the message
}
other => other?,
} Prevention
- Always copy the full hash from rtk's output when recalling.
- Use a minimum prefix length that scales with store size (e.g. 8+ chars).
- Handle the ambiguity error by parsing the matching hashes out of the message and retrying with a longer prefix.
When it happens
Trigger: Calling recall (via `run_recall`) with a hash argument shorter than needed to uniquely identify one row in the SQLite recall table — i.e. the prefix matches 2+ stored hashes. Tests exercise it by storing multiple entries whose hashes share a prefix and then calling load_by_hash with that shared prefix.
Common situations: Copy-pasting a truncated hash from a log or shell history; querying with a very short prefix (1-3 chars) when the store has accumulated many entries; hashing collisions across similar commands recorded in the same DB.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Antigravity is project-scoped. Use: rtk init --agent…
- at least one of install_claude or install_opencode must be…
- bunx requires a command argument
- --codex cannot be combined with --auto-patch
- --codex cannot be combined with --claude-md
AI-assisted analysis of rtk-ai/rtk@0924356b4c (2026-09-19).
Data as JSON: /api/errors/055ccdd72714d6e9.
Report an issue: GitHub.
Appendix: source
Thrown at src/core/retriever.rs:610
fn load_by_hash(conn: &Connection, hash: &str) -> Result<Option<Row>> {
let exact = format!("SELECT {SELECT_COLS} FROM recall WHERE hash = ?1");
if let Some(row) = conn.query_row(&exact, params![hash], map_row).optional()? {
return Ok(Some(row));
}
let mut stmt = conn.prepare(
"SELECT hash FROM recall WHERE substr(hash, 1, length(?1)) = ?1 ORDER BY hash ASC",
)?;
let candidates: Vec<String> = stmt
.query_map(params![hash], |r| r.get(0))?
.filter_map(|r| r.ok())
.collect();
match candidates.as_slice() {
[] => Ok(None),
[only] => {
let sql = format!("SELECT {SELECT_COLS} FROM recall WHERE hash = ?1");
Ok(conn.query_row(&sql, params![only], map_row).optional()?)
}
many => anyhow::bail!(
"ambiguous hash prefix '{hash}': matches {}",
many.join(", ")
),
}
}
fn decode(row: &Row) -> Result<Vec<u8>> {
match row.codec.as_str() {
"gzip" => gunzip(&row.blob),
_ => Ok(row.blob.clone()),
}
}
pub struct RecallArgs<'a> {
pub hash: Option<&'a str>,
pub full: bool,
pub from: Option<usize>,
pub lines: Option<usize>,View on GitHub (pinned to 0924356b4c)