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

  1. Regenerate in canonical format: `sha256sum ~/.claude/hooks/rtk-rewrite.sh > ~/.claude/hooks/rtk-rewrite.sh.sha256`
  2. Or re-run `rtk init -g` so rtk rewrites both the hook and its hash consistently
  3. 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

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


AI-assisted analysis of rtk-ai/rtk@d977e1c316 (2026-08-16). Data as JSON: /api/errors/98f80228eb82baeb. Report an issue: GitHub.