{"record":{"id":"22af4815b84ca4db","repo":"AlistGo/alist","slug":"invalid-file-name","errorCode":null,"errorMessage":"invalid file_name","messagePattern":"invalid file_name","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"server/handles/label_file_binding.go","lineNumber":40,"sourceCode":"\tContent []T   `json:\"content\"`\n\tTotal   int64 `json:\"total\"`\n}\n\ntype restoreLabelBindingsReq struct {\n\tKeepIDs  bool                     `json:\"keep_ids\"`\n\tOverride bool                     `json:\"override\"`\n\tBindings []model.LabelFileBinding `json:\"bindings\"`\n}\n\nfunc GetLabelByFileName(c *gin.Context) {\n\tfileName := c.Query(\"file_name\")\n\tif fileName == \"\" {\n\t\tcommon.ErrorResp(c, errors.New(\"file_name must not empty\"), 400)\n\t\treturn\n\t}\n\tdecodedFileName, err := url.QueryUnescape(fileName)\n\tif err != nil {\n\t\tcommon.ErrorResp(c, errors.New(\"invalid file_name\"), 400)\n\t\treturn\n\t}\n\tfmt.Println(\">>> 原始 fileName:\", fileName)\n\tfmt.Println(\">>> 解码后 fileName:\", decodedFileName)\n\tuserObj, ok := c.Value(\"user\").(*model.User)\n\tif !ok {\n\t\tcommon.ErrorStrResp(c, \"user invalid\", 401)\n\t\treturn\n\t}\n\tlabels, err := op.GetLabelByFileName(userObj.ID, decodedFileName)\n\tif err != nil {\n\t\tcommon.ErrorResp(c, err, 500, true)\n\t\treturn\n\t}\n\tcommon.SuccessResp(c, labels)\n}\n\nfunc CreateLabelFileBinDing(c *gin.Context) {","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/server/handles/label_file_binding.go#L22-L58","documentation":"Returned by GetLabelByFileName when url.QueryUnescape fails on the file_name query value, meaning the value contains a malformed percent-escape sequence (e.g. a lone '%' or invalid hex like '%zz'). gin already URL-decodes query parameters once, so this second decode expects the caller to have double-encoded the filename.","triggerScenarios":"Passing file_name with a raw '%' not part of a valid escape, or passing a once-encoded value that gin already decoded leaving stray escapes; e.g. ?file_name=100%progress.","commonSituations":"Filenames containing literal percent signs (progress trackers, '50%off.pdf'); clients that pre-encode the value while gin encodes/decodes it again, producing half-decoded input; mixing encoded slashes incorrectly.","solutions":["URL-encode the filename client-side so every '%' becomes '%25' before it reaches the query string","If the filename has no special characters, pass it plain (but still encode '%' and '&')","For filenames with '%' or '+', verify the exact bytes received server-side to debug double-encoding"],"exampleFix":"// before\nGET /api/label/file?file_name=/reports/100%off.pdf\n// after\nGET /api/label/file?file_name=%2Freports%2F100%25off.pdf","handlingStrategy":"validation","validationCode":"// Pre-validate the escape sequence the server will QueryUnescape\nvar badEscape = regexp.MustCompile(`%(?![0-9A-Fa-f]{2})`)\nif badEscape.MatchString(fileName) {\n    fileName = url.QueryEscape(fileName)\n}","typeGuard":null,"tryCatchPattern":"if resp.StatusCode() == 400 && strings.Contains(resp.String(), \"invalid file_name\") {\n    // re-send with full percent-encoding of the filename\n}","preventionTips":["Always URL-encode the file_name value; double-encode if the transport also decodes","Pay special attention to filenames containing '%' or '+'","Round-trip test encode/decode for exotic filenames in the client's test suite"],"tags":["labels","http-api","url-encoding","validation"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}