{"record":{"id":"c11b2bac2ed7254f","repo":"Tencent/WeKnora","slug":"failed-to-delete-file-w","errorCode":null,"errorMessage":"failed to delete file: %w","messagePattern":"failed to delete file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/file/local.go","lineNumber":150,"sourceCode":"}\n\n// DeleteFile removes a file from the local file system\n// Returns an error if deletion fails\n// 路径必须在 baseDir 下，防止路径遍历（如 ../../）\nfunc (s *localFileService) DeleteFile(ctx context.Context, filePath string) error {\n\tlogger.Infof(ctx, \"Deleting file: %s\", filePath)\n\n\tcandidate := s.normalizePathForBase(filePath)\n\tresolved, err := secutils.SafePathUnderBase(s.baseDir, candidate)\n\tif err != nil {\n\t\tlogger.Errorf(ctx, \"Path traversal denied for DeleteFile: %v\", err)\n\t\treturn fmt.Errorf(\"invalid file path: %w\", err)\n\t}\n\n\terr = os.Remove(resolved)\n\tif err != nil {\n\t\tlogger.Errorf(ctx, \"Failed to delete file: %v\", err)\n\t\treturn fmt.Errorf(\"failed to delete file: %w\", err)\n\t}\n\n\tlogger.Info(ctx, \"File deleted successfully\")\n\treturn nil\n}\n\n// CopyFile copies an existing local object to a new knowledge-owned object.\n// The destination uses the same layout as SaveFile (baseDir/{tenantID}/{knowledgeID}/{unique}{ext}),\n// and the copy is a real byte-for-byte copy (no hardlink) so deleting the source\n// never affects it. Returns ErrCrossBackendCopy when srcPath is not a local path.\nfunc (s *localFileService) CopyFile(ctx context.Context,\n\tsrcPath string, tenantID uint64, knowledgeID string,\n) (string, error) {\n\t// Only local paths are accepted. A provider scheme other than local://\n\t// (e.g. s3://, minio://) means a cross-backend copy, which this service\n\t// does not support. Legacy bare/absolute paths have no scheme and pass.\n\tif i := strings.Index(srcPath, \"://\"); i >= 0 && srcPath[:i+3] != localScheme {\n\t\treturn \"\", fmt.Errorf(\"local file service cannot copy %q: %w\", srcPath, ErrCrossBackendCopy)","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/local.go#L132-L168","documentation":"DeleteFile wraps the error from os.Remove(resolved) after path validation succeeded, meaning the file is inside baseDir but could not be removed. The wrapped OS error distinguishes a missing file (ENOENT), a permission problem (EACCES/EPERM), or a directory-not-empty case. Nothing is deleted when this fires.","triggerScenarios":"os.Remove fails because the file does not exist (already deleted or never saved), the service user lacks write permission on the containing directory, the path resolves to a non-empty directory, or the volume is read-only.","commonSituations":"Double-delete from a retry or concurrent requests; storage volume mounted read-only; ownership drift after container restart; cleanup job racing with an explicit delete.","solutions":["Treat os.ErrNotExist as success/idempotent delete in the caller (errors.Is(err, fs.ErrNotExist))","Check directory write permission on the file's parent (rm requires write on the dir) and fix ownership","Verify the storage volume is mounted read-write","Skip deleting if a concurrent cleanup job may have already removed the file"],"exampleFix":"// before: double-delete surfaces a confusing error\nif err := svc.DeleteFile(ctx, path); err != nil {\n\treturn err\n}\n// after: make delete idempotent\nif err := svc.DeleteFile(ctx, path); err != nil && !errors.Is(err, os.ErrNotExist) {\n\treturn err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := svc.DeleteFile(ctx, path)\nif err != nil {\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn nil // idempotent delete\n\t}\n\treturn fmt.Errorf(\"failed to delete file: %w\", err)\n}","preventionTips":["Make deletes idempotent by treating ErrNotExist as success","Ensure the service user can write to the file's parent directory","Mount the storage volume read-write and verify after deploys","Avoid concurrent delete + cleanup jobs on the same files"],"tags":["go","file-io","filesystem","deletion"],"backgroundTag":"file-delete-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}