rtk-ai/rtk · error · anyhow::Error
Invalid SHA-256 hash in {}
Error message
Invalid SHA-256 hash in {} What it means
After the two-space split succeeds, the first field must be exactly 64 ASCII hex characters (a SHA-256 digest). This bail means the digest itself is wrong: typically an md5/sha1 digest (32/40 chars), a truncated hash, or placeholder text sitting in the hash slot of the .sha256 sidecar.
Source
Thrown at src/hooks/integrity.rs:190
.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))
}
/// Run integrity check and print results (for `rtk verify` subcommand)
pub fn run_verify(verbose: u8) -> Result<()> {
let result = report_hook_status(verbose);
report_data_dir_privacy();
result
}
fn report_hook_status(verbose: u8) -> Result<()> {View on GitHub (pinned to d977e1c316)
Solutions
- Regenerate with sha256sum: `sha256sum <hook path> > <hook path>.sha256`
- Or re-run `rtk init -g` to have rtk rewrite the hook and its stored hash together
- If you intentionally modified the hook script, prefer re-init over hand-editing the hash so the later content comparison stays meaningful
Example fix
# before: md5 digest (32 hex chars) in the hash slot 5d41402a... rtk-rewrite.sh # after sha256sum ~/.claude/hooks/rtk-rewrite.sh > ~/.claude/hooks/rtk-rewrite.sh.sha256
Defensive patterns
Strategy: validation
Validate before calling
# bash: assert the digest is 64 hex chars (not md5/sha1)
head -1 "$SHA" | grep -qE '^[0-9a-fA-F]{64} ' || { echo 'not a SHA-256 digest' >&2; exit 2; } Type guard
// Rust: narrow to a 64-hex SHA-256 digest field
fn is_sha256_digest(field: &str) -> bool {
field.len() == 64 && field.chars().all(|c| c.is_ascii_hexdigit())
} Prevention
- Standardize hash generation on sha256sum in all scripts
- Treat a wrong-length digest as a red flag for the generating pipeline, not just a bad file
- Let rtk manage its own integrity files via re-init instead of editing them
When it happens
Trigger: Hash file produced with `md5sum`/`sha1sum`, a templating variable that never expanded, or manual edits — caught at src/hooks/integrity.rs:192 during `rtk verify`.
Common situations: Scripts defaulting to md5; LLM-generated dotfiles containing a placeholder hash; copy-paste that dropped characters.
Related errors
- Invalid hash format in {} (expected 'hash filename')
- {} 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/4587f6f2f8786fe4.
Report an issue: GitHub.