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

  1. Regenerate with sha256sum: `sha256sum <hook path> > <hook path>.sha256`
  2. Or re-run `rtk init -g` to have rtk rewrite the hook and its stored hash together
  3. 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

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


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