{"record":{"id":"999103b4cef6c30a","repo":"siyuan-note/siyuan","slug":"invalid-asset-filename","errorCode":null,"errorMessage":"invalid asset filename","messagePattern":"invalid asset filename","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/upload.go","lineNumber":54,"sourceCode":"\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\n// InsertAssetBytes 将内存中的资源直接写入目标文档资源目录，避免生成内容经过明文临时文件。\nfunc InsertAssetBytes(id, fileName string, data []byte) (assetPath string, created bool, err error) {\n\tbt := treenode.GetBlockTree(id)\n\tif bt == nil {\n\t\treturn \"\", false, errors.New(Conf.Language(71))\n\t}\n\tif len(data) == 0 {\n\t\treturn \"\", false, errors.New(\"asset data is empty\")\n\t}\n\n\tbaseName := filepath.Base(fileName)\n\tfName := util.FilterUploadFileName(baseName)\n\text := strings.ToLower(filepath.Ext(fName))\n\tfName = strings.TrimSuffix(fName, filepath.Ext(fName)) + ext\n\tif fName == \"\" || fName == \".\" || ext == \"\" {\n\t\treturn \"\", false, errors.New(\"invalid asset filename\")\n\t}\n\n\tdocDirLocalPath := filepath.Join(util.DataDir, bt.BoxID, path.Dir(bt.Path))\n\tassetsDirPath := getAssetsDir(filepath.Join(util.DataDir, bt.BoxID), docDirLocalPath)\n\tif err = os.MkdirAll(assetsDirPath, 0755); err != nil {\n\t\treturn \"\", false, err\n\t}\n\n\treader := bytes.NewReader(data)\n\thash, err := util.GetEtagByHandle(reader, int64(len(data)))\n\tif err != nil {\n\t\treturn \"\", false, err\n\t}\n\tif existAssetPath := GetAssetPathByHash(hash, bt.BoxID); existAssetPath != \"\" {\n\t\toriginalName := util.RemoveID(filepath.Base(existAssetPath))\n\t\tif strings.EqualFold(fName, originalName) {\n\t\t\treturn strings.TrimPrefix(existAssetPath, \"/\"), false, nil\n\t\t}","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/upload.go#L36-L72","documentation":"Thrown by InsertAssetBytes when the filename, after sanitization by util.FilterUploadFileName and extension normalization, is empty, is just a dot, or has no extension. The function requires a valid base name with an extension to compute the on-disk asset filename via util.AssetName or encryptedAssetName.","triggerScenarios":"Passing a fileName that is empty, consists only of invalid characters stripped by FilterUploadFileName, has no file extension, or normalizes to '.' after processing. Example: fileName = \"...\" or fileName = \"file\" (no extension).","commonSituations":"Client-side code constructs a filename programmatically and forgets the extension; a download pipeline strips the extension; the original filename contained only characters that FilterUploadFileName removes.","solutions":["Ensure the fileName parameter includes a proper extension (e.g., 'image.png')","If the source filename is unreliable, derive the extension from the MIME type before calling InsertAssetBytes","Sanitize the filename client-side and reject filenames without extensions before sending"],"exampleFix":"// before\nassetPath, _, err := model.InsertAssetBytes(id, \"screenshot\", data)\n\n// after\next := mimeToExt(mimeType) // e.g. \"png\"\nassetPath, _, err := model.InsertAssetBytes(id, \"screenshot.\"+ext, data)","handlingStrategy":"validation","validationCode":"ext := filepath.Ext(fileName)\nif ext == \"\" {\n    ext = mimeToExt(contentType) // derive from MIME type\n    fileName = fileName + \".\" + ext\n}\nif fileName == \"\" || ext == \"\" {\n    return errors.New(\"asset filename must have a valid extension\")\n}\nassetPath, _, err := model.InsertAssetBytes(id, fileName, data)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include a file extension in asset filenames","When the source filename is unreliable, derive the extension from the MIME/content-type","Sanitize filenames client-side and reject those without extensions"],"tags":["asset","filename","upload","validation","kernel"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}