{"record":{"id":"71401d593284725a","repo":"cloudreve/cloudreve","slug":"mismatched-id-type","errorCode":null,"errorMessage":"mismatched ID type.","messagePattern":"mismatched ID type\\.","errorType":"validation","errorClass":"ErrTypeNotMatch","httpStatus":null,"severity":"error","filePath":"pkg/hashid/hash.go","lineNumber":29,"sourceCode":"\tShareID  = iota // 分享\n\tUserID          // 用户\n\tFileID          // 文件ID\n\tFolderID        // 目录ID\n\tTagID           // 标签ID\n\tPolicyID        // 存储策略ID\n\tSourceLinkID\n\tGroupID\n\tEntityID\n\tAuditLogID\n\tNodeID\n\tTaskID\n\tDavAccountID\n\tPaymentID\n)\n\nvar (\n\t// ErrTypeNotMatch ID类型不匹配\n\tErrTypeNotMatch = errors.New(\"mismatched ID type.\")\n)\n\ntype Encoder interface {\n\tEncode(v []int) (string, error)\n\tDecode(raw string, t int) (int, error)\n}\n\n// ObjectIDCtx define key for decoded hash ID.\ntype (\n\tObjectIDCtx struct{}\n\tEncodeFunc  func(encoder Encoder, uid int) string\n)\n\ntype hashEncoder struct {\n\th *hashids.HashID\n}\n\nfunc New(salt string) (Encoder, error) {","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/cloudreve/cloudreve/blob/20c95ad73f3a8bcb72887fea91ff31ab24fa1011/pkg/hashid/hash.go#L11-L47","documentation":"hashid.ErrTypeNotMatch is returned by hashEncoder.Decode when the decoded hashids payload does not have exactly two numbers or the second number (the embedded type tag) differs from the requested type t. Cloudreve encodes every public ID as a pair [id, TypeID] (ShareID=0, UserID=1, FileID=2, ...), so a hash string is only valid for the one type it was created with.","triggerScenarios":"Calling encoder.Decode(raw, hashid.FileID) on a hash that was generated with EncodeUserID/EncodeShareID/etc.; calling Decode on an arbitrary string that happens to decode to a non-2-element slice; mixing hashes between API endpoints (passing a share ID where a file ID is expected in a URL parameter).","commonSituations":"Frontend sends the wrong ID field in a request (e.g. uses the share's hash in a file-scoped route); API consumers reuse IDs from one resource type against another endpoint; changing hashid type constants order between versions while keeping old hashes; testing Decode with placeholder strings.","solutions":["Check which encoder helper produced the hash (EncodeFileID, EncodeShareID, ...) and call Decode with the matching type constant","Fix the client/frontend to send the ID from the correct field of the API response","If you changed the iota order of the ID constants, revert it or migrate stored hashes"],"exampleFix":"// before\nid, err := hash.Decode(rawID, hashid.UserID) // rawID is actually a file hash\n\n// after\nid, err := hash.Decode(rawID, hashid.FileID)","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Go: distinguish type mismatch from malformed hash and map to 400\nid, err := encoder.Decode(raw, hashid.FileID)\nswitch {\ncase err == nil:\ncase errors.Is(err, hashid.ErrTypeNotMatch):\n    return serializer.Err(\n        code.CodeParamError,\n        \"ID does not belong to this resource type\",\n        nil,\n    )\ndefault:\n    return serializer.Err(\n        code.CodeInvalidRefer,\n        \"malformed ID\",\n        nil,\n    )\n}","preventionTips":["Keep a single source of truth mapping each API route/param to its hashid type constant","Always generate public IDs with the EncodeXxxID helpers so the type tag is embedded correctly","Never reorder the hashid iota constants; append new types only at the end","In integration tests, verify Decode rejects hashes of every other type"],"tags":["hashid","id-encoding","api","validation"],"backgroundTag":null,"analyzedSha":"20c95ad73f3a8bcb72887fea91ff31ab24fa1011","analyzedAt":"2026-08-16T01:42:55.403Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}