{"record":{"id":"2ed4958c6c3084a3","repo":"kopia/kopia","slug":"invalid-content-hash","errorCode":null,"errorMessage":"invalid content hash","messagePattern":"invalid content hash","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"repo/content/index/id.go","lineNumber":243,"sourceCode":"\n\tif len(s)%2 == 1 {\n\t\tid.prefix = s[0]\n\n\t\tif id.prefix < 'g' || id.prefix > 'z' {\n\t\t\treturn id, errors.New(\"invalid content prefix\")\n\t\t}\n\n\t\ts = s[1:]\n\t}\n\n\tif len(s) > 2*len(id.data) {\n\t\treturn id, errors.New(\"hash too long\")\n\t}\n\n\tn, err := hex.Decode(id.data[:], []byte(s))\n\tswitch {\n\tcase err != nil:\n\t\treturn id, errors.Wrap(err, \"invalid content hash\")\n\tcase n == 0:\n\t\treturn id, errors.Errorf(\"id too short: %q\", s0)\n\tcase n > len(id.data):\n\t\timpossible.PanicOnError(errors.Errorf(\"id too large: %d, %q\", n, s))\n\t}\n\n\t_ = uint(maxUInt8 - len(id.data))\n\tid.idLen = byte(n) //nolint:gosec // n <= len(id.data) <= 255\n\n\treturn id, nil\n}\n","sourceCodeStart":225,"sourceCodeEnd":255,"githubUrl":"https://github.com/kopia/kopia/blob/82495e54b584c1ef6073c9e1be048f57f8aef078/repo/content/index/id.go#L225-L255","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nid, err := ParseID(userInput) // may contain non-hex chars\n// after\ntrimmed := strings.TrimSpace(userInput)\nif !isHexString(trimmed) || len(trimmed)%2 != 0 {\n    return fmt.Errorf(\"not a valid hex ID: %q\", trimmed)\n}\nid, err := ParseID(trimmed)","handlingStrategy":"validation","validationCode":"func isHexID(s string) bool {\n    if len(s) == 0 || len(s)%2 != 0 { return false }\n    for _, c := range s {\n        if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { return false }\n    }\n    return true\n}\nif isHexID(s) { id, err := ParseID(s) }","typeGuard":"func looksLikeContentID(s string) bool { return len(s) > 0 && isHexString(s) }","tryCatchPattern":"id, err := ParseID(s)\nif err != nil && strings.Contains(err.Error(), \"invalid content hash\") {\n    return fmt.Errorf(\"ID %q is not valid hex\", s)\n}","preventionTips":["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)"],"tags":["hex-decode","content-id","go","parsing"],"backgroundTag":"invalid-identifier-format","analyzedSha":"82495e54b584c1ef6073c9e1be048f57f8aef078","analyzedAt":"2026-09-07T20:35:21.689Z","contentChangedAt":"2026-09-07T20:35:21.689Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}