{"record":{"id":"b05ebf4c3ef35c40","repo":"flipped-aurora/gin-vue-admin","slug":"error-b05ebf","errorCode":null,"errorMessage":"文件不存在","messagePattern":"文件不存在","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/utils/upload/local.go","lineNumber":91,"sourceCode":"\n//@author: [piexlmax](https://github.com/piexlmax)\n//@author: [ccfish86](https://github.com/ccfish86)\n//@author: [SliverHorn](https://github.com/SliverHorn)\n//@object: *Local\n//@function: DeleteFile\n//@description: 删除文件\n//@param: key string\n//@return: error\n\nfunc (l *Local) DeleteFile(ctx context.Context, key string) error {\n\tp, err := l.localPath(ctx, key)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// 检查文件是否存在\n\tif _, err := os.Stat(p); os.IsNotExist(err) {\n\t\treturn errors.New(\"文件不存在\")\n\t}\n\n\t// 使用文件锁防止并发删除\n\tmu.Lock()\n\tdefer mu.Unlock()\n\n\terr = os.Remove(p)\n\tif err != nil {\n\t\treturn errors.New(\"文件删除失败: \" + err.Error())\n\t}\n\n\treturn nil\n}\n\n// localPath 校验 key 并拼接出本地存储的绝对路径，复用 DeleteFile 中的路径穿越防护逻辑。\nfunc (*Local) localPath(ctx context.Context, key string) (string, error) {\n\t// 检查 key 是否为空\n\tif key == \"\" {","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/utils/upload/local.go#L73-L109","documentation":"This error is returned by Local.DeleteFile when os.Stat reports that the file corresponding to the given key does not exist under the local store path. The library checks existence before acquiring the delete lock so callers get a clear 'file missing' signal instead of an opaque remove error.","triggerScenarios":"Calling DeleteFile (directly or via DeleteFiles) with a key that was never uploaded, was already deleted, or was deleted outside the app; also when StorePath configuration changed so the key no longer resolves to the original location.","commonSituations":"Double-delete race where two requests delete the same key; cleanup jobs running concurrently with user deletes; switching environments (dev/prod) where the DB still references files absent on disk; typo'd key from a stale frontend cache.","solutions":["Treat this as an idempotent success if your delete flow tolerates missing files (check the error message for 文件不存在 and skip).","Verify the key exists first with the Exists() method, or list files via ListFiles to confirm the key.","Confirm local.store-path in config matches the environment where the file was originally uploaded.","If it happens in batch deletes, filter DeleteFiles input against Exists() results beforehand."],"exampleFix":"// before\nerr := local.DeleteFile(ctx, key)\n// after\nexists, err := local.Exists(ctx, key)\nif err == nil && exists {\n    err = local.DeleteFile(ctx, key)\n} // missing files are silently skipped (idempotent delete)","handlingStrategy":"validation","validationCode":"exists, err := local.Exists(ctx, key)\nif err != nil {\n    return err\n}\nif !exists {\n    return nil // already gone; idempotent no-op\n}","typeGuard":null,"tryCatchPattern":"err := local.DeleteFile(ctx, key)\nif err != nil && err.Error() == \"文件不存在\" {\n    return nil // treat as success (idempotent delete)\n}\nif err != nil {\n    return err\n}","preventionTips":["Treat file deletion as idempotent — missing means already deleted, not failure.","Check Exists() before deleting in interactive flows.","Keep config's store-path consistent across replicas/environments sharing a DB.","Serialize deletes for the same key (or accept races) in cleanup jobs."],"tags":["filesystem","file-deletion","go"],"backgroundTag":"file-not-found","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}