{"record":{"id":"030523a55f1eea1d","repo":"flipped-aurora/gin-vue-admin","slug":"obs-delete-s-failed-code-s-message-s","errorCode":null,"errorMessage":"obs delete %s failed: code=%s, message=%s","messagePattern":"obs delete (.+?) failed: code=(.+?), message=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/utils/upload/obs.go","lineNumber":134,"sourceCode":"\tfor _, k := range keys {\n\t\tobjects = append(objects, obs.ObjectToDelete{Key: k})\n\t}\n\n\toutput, err := client.DeleteObjects(&obs.DeleteObjectsInput{\n\t\tBucket:  global.GVA_CONFIG.HuaWeiObs.Bucket,\n\t\tObjects: objects,\n\t\tQuiet:   true,\n\t})\n\tif err != nil {\n\t\tlogger.WithCtx(ctx).Mod(\"upload\").Err(err).Error(\"obs DeleteObjects 失败\")\n\t\treturn nil, err\n\t}\n\n\tif output != nil {\n\t\tfor _, e := range output.Errors {\n\t\t\tfailed = append(failed, DeleteFailure{\n\t\t\t\tKey: e.Key,\n\t\t\t\tErr: fmt.Errorf(\"obs delete %s failed: code=%s, message=%s\", e.Key, e.Code, e.Message),\n\t\t\t})\n\t\t}\n\t}\n\treturn failed, nil\n}\n\n// ListFiles 按前缀列举对象，marker 分页：Marker=cursor，NextMarker→nextCursor，IsTruncated→hasMore。\nfunc (o *Obs) ListFiles(ctx context.Context, prefix, cursor string, limit int) (files []FileInfo, nextCursor string, hasMore bool, err error) {\n\tif limit <= 0 {\n\t\tlimit = 100\n\t}\n\n\tclient, err := NewHuaWeiObsClient()\n\tif err != nil {\n\t\tlogger.WithCtx(ctx).Mod(\"upload\").Err(err).Error(\"获取华为对象存储对象失败\")\n\t\treturn nil, \"\", false, pkgerrors.Wrap(err, \"获取华为对象存储对象失败!\")\n\t}\n","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/utils/upload/obs.go#L116-L152","documentation":"The Huawei OBS DeleteFiles implementation performs a batch delete; the OBS response can carry per-object Errors even when the overall call succeeds. For each failed entry a DeleteFailure is returned with \"obs delete %s failed: code=%s, message=%s\" including the key, OBS error code, and message. It surfaces partial batch-delete failures instead of silently dropping them.","triggerScenarios":"Calling DeleteFiles on the OBS uploader where output.Errors is non-empty — e.g. deleting keys that don't exist with a policy that errors, insufficient IAM permissions, or keys locked/protected in the bucket.","commonSituations":"Wrong bucket/credentials with limited delete rights; objects already deleted by another process; OBS-specific error codes like AccessDenied or InvalidArgument in the multi-delete response.","solutions":["Read the code= and message= fields per DeleteFailure to identify the OBS error and the affected key.","Verify the access key has OBS delete permission (IAM policy) for the bucket.","Retry deleting only the failed keys after fixing permissions or confirming the objects' existence.","Handle per-key failures individually — other keys in the batch may have succeeded."],"exampleFix":"// before\nfailed, _ := uploader.DeleteFiles(ctx, keys) // ignores per-key failures\n\n// after\nfailed, err := uploader.DeleteFiles(ctx, keys)\nfor _, f := range failed {\n    log.Printf(\"retry delete %s: %v\", f.Key, f.Err)\n    _ = retryDelete(ctx, uploader, f.Key)\n}","handlingStrategy":"try-catch","validationCode":"// best effort: check keys exist / are deletable before batch delete\nfor _, key := range keys {\n    if key == \"\" || strings.HasPrefix(key, \"/\") {\n        return fmt.Errorf(\"invalid key for OBS delete: %q\", key)\n    }\n}","typeGuard":null,"tryCatchPattern":"failed, err := uploader.DeleteFiles(ctx, keys)\nif err != nil {\n    return err\n}\nfor _, f := range failed {\n    var delErr *upload.DeleteFailure\n    log.Printf(\"obs delete failed key=%s: %v\", f.Key, f.Err)\n    // inspect code= in f.Err to decide retry vs skip\n}","preventionTips":["Grant IAM delete permissions for the bucket to the access key","Treat batch delete as partial: always inspect the failed list","Log per-key failures for later reconciliation","Pre-validate key names before batch operations"],"tags":["obs","object-storage","delete","cloud"],"backgroundTag":"object-delete-failed","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}