{"record":{"id":"055ccdd72714d6e9","repo":"rtk-ai/rtk","slug":"ambiguous-hash-prefix-hash-matches","errorCode":null,"errorMessage":"ambiguous hash prefix '{hash}': matches {}","messagePattern":"ambiguous hash prefix '(.+?)': matches (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/core/retriever.rs","lineNumber":610,"sourceCode":"fn load_by_hash(conn: &Connection, hash: &str) -> Result<Option<Row>> {\n    let exact = format!(\"SELECT {SELECT_COLS} FROM recall WHERE hash = ?1\");\n    if let Some(row) = conn.query_row(&exact, params![hash], map_row).optional()? {\n        return Ok(Some(row));\n    }\n    let mut stmt = conn.prepare(\n        \"SELECT hash FROM recall WHERE substr(hash, 1, length(?1)) = ?1 ORDER BY hash ASC\",\n    )?;\n    let candidates: Vec<String> = stmt\n        .query_map(params![hash], |r| r.get(0))?\n        .filter_map(|r| r.ok())\n        .collect();\n    match candidates.as_slice() {\n        [] => Ok(None),\n        [only] => {\n            let sql = format!(\"SELECT {SELECT_COLS} FROM recall WHERE hash = ?1\");\n            Ok(conn.query_row(&sql, params![only], map_row).optional()?)\n        }\n        many => anyhow::bail!(\n            \"ambiguous hash prefix '{hash}': matches {}\",\n            many.join(\", \")\n        ),\n    }\n}\n\nfn decode(row: &Row) -> Result<Vec<u8>> {\n    match row.codec.as_str() {\n        \"gzip\" => gunzip(&row.blob),\n        _ => Ok(row.blob.clone()),\n    }\n}\n\npub struct RecallArgs<'a> {\n    pub hash: Option<&'a str>,\n    pub full: bool,\n    pub from: Option<usize>,\n    pub lines: Option<usize>,","sourceCodeStart":592,"sourceCodeEnd":628,"githubUrl":"https://github.com/rtk-ai/rtk/blob/0924356b4caba4989607227b7c8824d3d8098719/src/core/retriever.rs#L592-L628","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nrtk recall ab1\n// error: ambiguous hash prefix 'ab1': matches ab12..., ab9f...\n\n// after\nrtk recall ab1234  # longer prefix, or the full hash","handlingStrategy":"validation","validationCode":"// Check prefix uniqueness before recalling\nlet matches: Vec<String> = list_recall_hashes()\n    .await?\n    .into_iter()\n    .filter(|h| h.starts_with(&prefix))\n    .collect();\nif matches.len() > 1 {\n    eprintln!(\"ambiguous prefix {prefix}: use one of {:?}\", matches);\n    return Ok(());\n}","typeGuard":"fn unique_prefix(prefix: &str, hashes: &[String]) -> Option<String> {\n    let m: Vec<_> = hashes.iter().filter(|h| h.starts_with(prefix)).collect();\n    match m.as_slice() {\n        [one] => Some((*one).clone()),\n        _ => None,\n    }\n}","tryCatchPattern":"match rtk_recall(&prefix) {\n    Err(e) if e.to_string().contains(\"ambiguous hash prefix\") => {\n        // retry with a longer prefix or pick a full hash from the message\n    }\n    other => other?,\n}","preventionTips":["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."],"tags":["hash","ambiguity","cli","database"],"backgroundTag":"invalid-identifier-format","analyzedSha":"0924356b4caba4989607227b7c8824d3d8098719","analyzedAt":"2026-09-19T19:25:29.612Z","contentChangedAt":"2026-09-19T19:25:29.612Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}