rtk-ai/rtk · error

Invalid hash format in

Error message

Invalid hash format in {} (expected 'hash  filename')

What it means

read_stored_hash parses a sha256sum-format checksum file: it expects a single line of '<hash> <filename>' separated by exactly two spaces. If splitting the first line on the two-space separator does not yield exactly 2 parts, the file's format is not what rtk wrote, and it bails so verification fails safely rather than comparing garbage.

Solutions

  1. Regenerate the hash file the way rtk expects: `cd <hook dir> && sha256sum rtk-rewrite.sh > rtk-rewrite.sh.sha256` (sha256sum emits the two-space separator)
  2. Inspect the file: `cat -A <hook>.sha256` to see hidden spacing; fix it to '<64-hex> <filename>' with exactly two spaces and a single line
  3. Reinstall the hook via rtk (e.g. `rtk init -g`) so rtk rewrites the checksum file itself

Example fix

// before (hand-written, single space)
abc123  rtk-rewrite.sh
// after
printf '%s  %s\n' "$(sha256sum rtk-rewrite.sh | cut -d' ' -f1)" "rtk-rewrite.sh" > rtk-rewrite.sh.sha256
Defensive patterns

Strategy: validation

Validate before calling

// validate a sha256sum file before running verify
awk 'NR==1 && split($0, a, "  ") == 2 { exit (length(a[1])==64 && a[1] ~ /^[0-9a-fA-F]+$/) ? 0 : 1 }' "$hook.sha256" || regen_checksums

Type guard

fn is_sha256sum_line(line: &str) -> bool {
    match line.splitn(2, "  ").collect::<Vec<_>>()[..] {
        [h, _f] => h.len() == 64 && h.chars().all(|c| c.is_ascii_hexdigit()),
        _ => false,
    }
}

Try / catch

match verify_hook_at(&path) {
    Err(e) if e.to_string().contains("Invalid hash format") => regenerate_checksum_file(&path)?,
    other => other?,
}

Prevention

When it happens

Trigger: Calling verify_hook_at on a hook whose .sha256 file was hand-edited, truncated, generated with a single-space separator (`sha256sum file > f` piped differently), or written by another tool with a different line layout; also when the file contains extra lines/whitespace such that splitn(2, " ") yields != 2 parts.

Common situations: Manually regenerating checksums with `shasum -a 256` variants that pad differently; editors stripping trailing spaces or normalizing double spaces; partial writes/corruption of the hash file in ~/.claude/hooks.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of rtk-ai/rtk@0924356b4c (2026-09-19). Data as JSON: /api/errors/98f80228eb82baeb. Report an issue: GitHub.

Appendix: 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 0924356b4c)