{"record":{"id":"7a62ca83c0ac0481","repo":"flipped-aurora/gin-vue-admin","slug":"function-client-deleteobjects-failed-err-7a62ca","errorCode":null,"errorMessage":"function client.DeleteObjects() failed, err:","messagePattern":"function client\\.DeleteObjects\\(\\) failed, err:","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/utils/upload/cloudflare_r2.go","lineNumber":133,"sourceCode":"\tobjects := make([]types.ObjectIdentifier, 0, len(keys))\n\tfor _, key := range keys {\n\t\tfullKey := key\n\t\t// 入参可能是短 key（不含前缀），也可能是 List 出来的全路径 key；统一拼接前缀时避免重复。\n\t\tif prefix != \"\" && !strings.HasPrefix(key, prefix+\"/\") {\n\t\t\tfullKey = prefix + \"/\" + key\n\t\t}\n\t\tobjects = append(objects, types.ObjectIdentifier{Key: aws.String(fullKey)})\n\t}\n\n\tout, err := client.DeleteObjects(ctx, &s3.DeleteObjectsInput{\n\t\tBucket: aws.String(bucket),\n\t\tDelete: &types.Delete{\n\t\t\tObjects: objects,\n\t\t\tQuiet:   aws.Bool(true),\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, errors.New(\"function client.DeleteObjects() failed, err:\" + err.Error())\n\t}\n\n\tfor _, e := range out.Errors {\n\t\tkey := \"\"\n\t\tif e.Key != nil {\n\t\t\tkey = *e.Key\n\t\t}\n\t\tmsg := \"unknown error\"\n\t\tif e.Message != nil {\n\t\t\tmsg = *e.Message\n\t\t}\n\t\tfailed = append(failed, DeleteFailure{Key: key, Err: errors.New(msg)})\n\t}\n\treturn failed, nil\n}\n\n// ListFiles 按前缀列举存储对象，cursor 映射到 ContinuationToken。\nfunc (c *CloudflareR2) ListFiles(ctx context.Context, prefix, cursor string, limit int) (files []FileInfo, nextCursor string, hasMore bool, err error) {","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/utils/upload/cloudflare_r2.go#L115-L151","documentation":"CloudflareR2.DeleteFiles performs a batch DeleteObjects (multi-object delete); an SDK-level failure of the batch request is wrapped in this message. This is distinct from per-key failures — it means the whole HTTP/API call failed and no batch result was produced.","triggerScenarios":"DeleteFiles(ctx, keys...) when the batch call exceeds request constraints (too many keys), the request payload fails signing/size limits, network to R2 fails, or credentials/permissions are invalid for the delete API.","commonSituations":"Batch size above limits (S3 caps multi-delete at 1000 keys; oversized payloads via proxies), token without write permission, R2 endpoint unreachable, malformed key names (leading slashes from path joins) causing request rejection.","solutions":["Cap each DeleteFiles batch at 1000 keys and chunk larger lists before calling","Inspect the wrapped err text: AccessDenied -> token permissions; MalformedXML/oversized -> batch too large; timeout -> network","Check keys for path-join artifacts (empty strings, double slashes) and sanitize before deleting","Verify endpoint/bucket config as in the single DeleteFile case","Retry the whole batch only after fixing the root cause; batch delete is not safely retryable blindly (though S3 multi-delete is idempotent per key)"],"exampleFix":"// before\nfailed, err := uploader.DeleteFiles(ctx, allKeys) // 5000 keys\n// after\nfor chunk := range slices.Chunk(allKeys, 1000) {\n    f, err := uploader.DeleteFiles(ctx, chunk)\n    ...\n}","handlingStrategy":"validation","validationCode":"if len(keys) > 1000 {\n    return errors.New(\"batch delete limited to 1000 keys per request\")\n}\nfor _, k := range keys {\n    if k == \"\" || strings.Contains(k, \"//\") {\n        return fmt.Errorf(\"invalid key in batch: %q\", k)\n    }\n}","typeGuard":"null","tryCatchPattern":"failed, err := uploader.DeleteFiles(ctx, chunk)\nif err != nil {\n    if strings.Contains(err.Error(), \"DeleteObjects() failed\") {\n        logger.Error(\"batch delete transport failure; chunk keys\", zap.Strings(\"keys\", chunk), zap.Error(err))\n    }\n    return err\n}\nfor _, f := range failed { logger.Warn(\"key delete failed\", zap.String(\"key\", f.Key)) }","preventionTips":["Chunk batches to <=1000 keys before calling DeleteFiles","Sanitize keys (trim slashes, reject empties) before batching","Confirm token write permissions before batch operations","Log per-key failures from DeleteFailure instead of failing the whole batch"],"tags":["cloudflare-r2","s3","batch-delete","storage"],"backgroundTag":"s3-batch-delete-failed","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}