rtk-ai/rtk · error · anyhow::Error
Invalid hash format in {} (expected 'hash filename')
Error message
Invalid hash format in {} (expected 'hash filename') What it means
rtk verify stores the rewrite hook's digest in sha256sum layout: '<64 hex chars> <filename>' with a two-space separator. read_stored_hash splits the first line on ' ' and bails when the split yields only one field, i.e. the .sha256 sidecar file is not in sha256sum format at all.
Source
Thrown at src/hooks/integrity.rs:182
}
/// Read the stored hash from the hash file.
///
/// Expects exact `sha256sum -c` format: `<64 hex> <filename>\n`
/// Rejects malformed files rather than silently accepting them.
fn read_stored_hash(path: &Path) -> Result<String> {
let content = fs::read_to_string(path)
.with_context(|| format!("Failed to read hash file: {}", path.display()))?;
let line = content
.lines()
.next()
.with_context(|| format!("Empty hash file: {}", path.display()))?;
// sha256sum format uses two-space separator: "<hash> <filename>"
let parts: Vec<&str> = line.splitn(2, " ").collect();
if parts.len() != 2 {
anyhow::bail!(
"Invalid hash format in {} (expected 'hash filename')",
path.display()
);
}
let hash = parts[0];
if hash.len() != 64 || !hash.chars().all(|c| c.is_ascii_hexdigit()) {
anyhow::bail!("Invalid SHA-256 hash in {}", path.display());
}
Ok(hash.to_string())
}
/// Resolve the default hook path (~/.claude/hooks/rtk-rewrite.sh)
pub fn resolve_hook_path() -> Result<PathBuf> {
resolve_claude_dir().map(|dir| dir.join(HOOKS_SUBDIR).join(REWRITE_HOOK_FILE))
}
View on GitHub (pinned to d977e1c316)
Solutions
- Regenerate in canonical format: `sha256sum ~/.claude/hooks/rtk-rewrite.sh > ~/.claude/hooks/rtk-rewrite.sh.sha256`
- Or re-run `rtk init -g` so rtk rewrites both the hook and its hash consistently
- Check the first line matches `<64 hex chars><two spaces><filename>`
Example fix
# before: single-space or mangled layout abc123... single-space-name # after sha256sum ~/.claude/hooks/rtk-rewrite.sh > ~/.claude/hooks/rtk-rewrite.sh.sha256
Defensive patterns
Strategy: validation
Validate before calling
# bash: pre-check the sha256sum sidecar layout before `rtk verify`
SHA=~/.claude/hooks/rtk-rewrite.sh.sha256
head -1 "$SHA" | grep -qE '^[0-9a-fA-F]{64} ' || { echo "bad sha256sum layout in $SHA" >&2; exit 2; } Type guard
// Rust: true when a line is valid sha256sum layout ('<64 hex> <filename>')
fn is_sha256sum_line(line: &str) -> bool {
let mut parts = line.splitn(2, " ");
matches!(parts.next(), Some(h) if h.len() == 64 && h.chars().all(|c| c.is_ascii_hexdigit()))
&& parts.next().is_some()
} Prevention
- Generate hash files only with `sha256sum`, never shasum/md5sum or hand edits
- Exclude rtk's .sha256 sidecars from dotfile templating that collapses whitespace
- Prefer re-running `rtk init -g` over manually regenerating integrity files
When it happens
Trigger: The hook's .sha256 sidecar (e.g. ~/.claude/hooks/rtk-rewrite.sh.sha256) was hand-edited, reformatted by a dotfile manager, or generated by a tool using a single space or a different layout; the next `rtk verify` fails at src/hooks/integrity.rs:184.
Common situations: chezmoi/stow templates rewriting hash files; CI regenerating hashes with `shasum` (macOS) or custom scripts; an empty or truncated file.
Related errors
- Invalid SHA-256 hash in {}
- {} filter(s) have no inline tests (use --require-all in CI)
- {} test(s) failed
AI-assisted analysis of rtk-ai/rtk@d977e1c316 (2026-08-16).
Data as JSON: /api/errors/98f80228eb82baeb.
Report an issue: GitHub.