kopia/kopia · error
invalid content hash
Error message
invalid content hash
What it means
ParseID converts a string ID back into an ID structure; after length checks it hex-decodes the remaining string into the fixed-size data buffer. If hex.Decode returns an error (odd-length string or non-hex characters), the error is wrapped as "invalid content hash". This means the ID string is not a valid hex encoding of a content hash.
Solutions
- Verify the ID string contains only hex characters [0-9a-f] and has even length before calling ParseID.
- Re-obtain the ID from its authoritative source (listing contents, manifest) instead of retyping it.
- Trim surrounding whitespace/quotes and undo any URL-encoding applied to the ID string.
- Check the data source (config, metadata blob) for corruption if the ID came from a file.
Example fix
// before
id, err := ParseID(userInput) // may contain non-hex chars
// after
trimmed := strings.TrimSpace(userInput)
if !isHexString(trimmed) || len(trimmed)%2 != 0 {
return fmt.Errorf("not a valid hex ID: %q", trimmed)
}
id, err := ParseID(trimmed) Defensive patterns
Strategy: validation
Validate before calling
func isHexID(s string) bool {
if len(s) == 0 || len(s)%2 != 0 { return false }
for _, c := range s {
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { return false }
}
return true
}
if isHexID(s) { id, err := ParseID(s) } Type guard
func looksLikeContentID(s string) bool { return len(s) > 0 && isHexString(s) } Try / catch
id, err := ParseID(s)
if err != nil && strings.Contains(err.Error(), "invalid content hash") {
return fmt.Errorf("ID %q is not valid hex", s)
} Prevention
- Always pass IDs as obtained from the library, never retyped
- Normalize casing and trim whitespace before parsing
- Validate hex-ness at input boundaries (CLI, HTTP params)
When it happens
Trigger: Calling content.ParseID(s) with a string containing non-hex characters (e.g. 'g'-'z') or an odd number of hex digits, after the length pre-checks pass.
Common situations: Hand-typed or truncated IDs pasted from logs; IDs mangled by shells or URLs; corrupted metadata files; parsing IDs from older/foreign formats that weren't hex-encoded.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07).
Data as JSON: /api/errors/2ed4958c6c3084a3.
Report an issue: GitHub.
Appendix: source
Thrown at repo/content/index/id.go:243
if len(s)%2 == 1 {
id.prefix = s[0]
if id.prefix < 'g' || id.prefix > 'z' {
return id, errors.New("invalid content prefix")
}
s = s[1:]
}
if len(s) > 2*len(id.data) {
return id, errors.New("hash too long")
}
n, err := hex.Decode(id.data[:], []byte(s))
switch {
case err != nil:
return id, errors.Wrap(err, "invalid content hash")
case n == 0:
return id, errors.Errorf("id too short: %q", s0)
case n > len(id.data):
impossible.PanicOnError(errors.Errorf("id too large: %d, %q", n, s))
}
_ = uint(maxUInt8 - len(id.data))
id.idLen = byte(n) //nolint:gosec // n <= len(id.data) <= 255
return id, nil
}
View on GitHub (pinned to 82495e54b5)