{"record":{"id":"c3a18e652a18184b","repo":"Tencent/WeKnora","slug":"failed-to-delete-file-w-c3a18e","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/s3.go","lineNumber":286,"sourceCode":"\t\treturn nil, fmt.Errorf(\"failed to get file from S3: %w\", err)\n\t}\n\n\treturn resp.Body, nil\n}\n\n// DeleteFile deletes a file\nfunc (s *s3FileService) DeleteFile(ctx context.Context, filePath string) error {\n\tobjectName, err := s.parseS3FilePath(filePath)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = s.client.DeleteObject(ctx, &s3.DeleteObjectInput{\n\t\tBucket: aws.String(s.bucketName),\n\t\tKey:    aws.String(objectName),\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to delete file: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// CopyFile copies an existing S3 object to a new knowledge-owned object using a\n// server-side CopyObject (no data leaves S3). The destination uses the same\n// layout as SaveFile. Returns ErrCrossBackendCopy when srcPath is not an s3:// path.\nfunc (s *s3FileService) CopyFile(ctx context.Context,\n\tsrcPath string, tenantID uint64, knowledgeID string,\n) (string, error) {\n\tsrcKey, err := s.parseS3FilePath(srcPath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"s3 copy rejected source %q: %w\", srcPath, ErrCrossBackendCopy)\n\t}\n\n\text := filepath.Ext(srcPath)\n\tdestKey := fmt.Sprintf(\"%s%d/%s/%s%s\", s.pathPrefix, tenantID, knowledgeID, uuid.New().String(), ext)","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/s3.go#L268-L304","documentation":"DeleteFile wraps errors from the S3 DeleteObject call. It indicates the delete request itself failed — permissions, connectivity, or bucket state — not that the object didn't exist (S3 delete of a missing key succeeds). The original S3 error is chained via %w.","triggerScenarios":"Calling DeleteFile when IAM credentials lack s3:DeleteObject on the bucket, the bucket has versioning with Object Lock / legal hold preventing deletion, or the S3 endpoint is unreachable.","commonSituations":"Read-only IAM role used by the service; S3 Object Lock or bucket policy denying DeleteObject; misconfigured endpoint; KMS key permission missing for SSE-KMS objects.","solutions":["Grant s3:DeleteObject on the bucket/prefix in the IAM policy","Check for Object Lock, retention policies, or deny statements in bucket policy","Unwrap with errors.As to read the exact S3 error code and act on it","Verify endpoint/region and network reachability from the host"],"exampleFix":"// before\nerr := fileSvc.DeleteFile(ctx, path)\n// after\nerr := fileSvc.DeleteFile(ctx, path)\nif err != nil {\n    var apiErr smithy.APIError\n    if errors.As(err, &apiErr) && apiErr.ErrorCode() == \"AccessDenied\" {\n        // log config/permission issue, don't retry\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := svc.DeleteFile(ctx, path)\nif err != nil {\n    var apiErr smithy.APIError\n    if errors.As(err, &apiErr) {\n        log.Printf(\"delete failed code=%s: %v\", apiErr.ErrorCode(), err)\n    }\n    return err\n}","preventionTips":["Grant s3:DeleteObject on the app's prefixes in IAM","Watch for Object Lock/retention policies on buckets used for deletable files","Don't retry AccessDenied errors; only retry transient/network errors","Treat delete of a missing key as success (S3 semantics)"],"tags":["aws","s3","permissions","storage"],"backgroundTag":"s3-deleteobject-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}