{"record":{"id":"5eabd5fdf0783a4b","repo":"AlistGo/alist","slug":"wrong-md5-hash","errorCode":null,"errorMessage":"wrong md5 hash","messagePattern":"wrong md5 hash","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/chunker/util.go","lineNumber":243,"sourceCode":"\tvar meta metadataJSON\n\tif err := json.Unmarshal(data, &meta); err != nil {\n\t\treturn nil, err\n\t}\n\tif meta.Version == nil || meta.Size == nil || meta.ChunkNum == nil {\n\t\treturn nil, errors.New(\"missing required field\")\n\t}\n\tif *meta.Version < 1 {\n\t\treturn nil, errors.New(\"wrong version\")\n\t}\n\tif *meta.Size < 0 {\n\t\treturn nil, errors.New(\"negative file size\")\n\t}\n\tif *meta.ChunkNum < 1 || *meta.ChunkNum > maxSafeChunkNumber {\n\t\treturn nil, errors.New(\"wrong number of chunks\")\n\t}\n\tif meta.MD5 != \"\" {\n\t\tif _, err := hex.DecodeString(meta.MD5); err != nil || len(meta.MD5) != 32 {\n\t\t\treturn nil, errors.New(\"wrong md5 hash\")\n\t\t}\n\t}\n\tif meta.SHA1 != \"\" {\n\t\tif _, err := hex.DecodeString(meta.SHA1); err != nil || len(meta.SHA1) != 40 {\n\t\t\treturn nil, errors.New(\"wrong sha1 hash\")\n\t\t}\n\t}\n\tif *meta.Version > chunkerMetadataVerion {\n\t\treturn nil, errors.New(\"unknown metadata version\")\n\t}\n\treturn &chunkMetadata{\n\t\tVersion: *meta.Version,\n\t\tSize:    *meta.Size,\n\t\tNChunks: *meta.ChunkNum,\n\t\tMD5:     meta.MD5,\n\t\tSHA1:    meta.SHA1,\n\t\tXactID:  meta.XactID,\n\t}, nil","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/chunker/util.go#L225-L261","documentation":"When the optional md5 field in metadata is non-empty, it must be exactly 32 lowercase-hex-decodable characters (a canonical MD5 digest). hex.DecodeString failing or a length != 32 yields 'wrong md5 hash'. This guards the integrity chain: the driver later compares this stored digest against chunks it reads.","triggerScenarios":"Metadata containing \"md5\":\"deadbeef\" (too short), mixed-case or non-hex characters, or a digest from a different algorithm (e.g. a 40-char SHA1 put in the md5 field). Raised when reading a chunked file with such metadata.","commonSituations":"Metadata written by scripts using SHA1/SHA256 in the md5 slot; uppercase hex digests; truncation of the field; copy-paste errors.","solutions":["Remove the incorrect md5 value from the metadata file (empty string is allowed and skips the check), or set it to the correct 32-char lowercase hex MD5","Prefer regenerating metadata by re-uploading the file through the driver","If your fork stores digests differently, keep the field empty rather than abusing it"],"exampleFix":"// before (metadata file content)\n{\"ver\":1,\"size\":1024,\"nchunks\":2,\"md5\":\"<40-char sha1>\"}\n// after\n{\"ver\":1,\"size\":1024,\"nchunks\":2,\"md5\":\"<32-char md5 hex>\"}","handlingStrategy":"type-guard","validationCode":"if meta.MD5 != \"\" {\n    if _, err := hex.DecodeString(meta.MD5); err != nil || len(meta.MD5) != 32 {\n        return fmt.Errorf(\"md5 must be 32 hex chars or empty\")\n    }\n}","typeGuard":"func isValidMD5Field(s string) bool {\n    if s == \"\" { return true }\n    _, err := hex.DecodeString(s)\n    return err == nil && len(s) == 32\n}","tryCatchPattern":null,"preventionTips":["Emit lowercase hex digests with the right field mapping (32->md5, 40->sha1)","Leave hash fields empty when the digest algorithm doesn't match"],"tags":["chunker","metadata","hash","integrity"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}