flipped-aurora/gin-vue-admin · error

function bucket.DeleteObjects() failed, err:

Error message

function bucket.DeleteObjects() failed, err:

What it means

Returned by AliyunOSS.DeleteFiles (server/utils/upload/aliyun_oss.go:109) when bucket.DeleteObjects(keys) — the OSS DeleteMultipleObjects API — fails after a valid bucket client was created. The request reached (or tried to reach) OSS and was rejected: causes include authentication, permission (oss:DeleteObject), request size limits, or network errors. Unlike the per-key comparison that follows, this error aborts the whole batch.

Source

Thrown at server/utils/upload/aliyun_oss.go:109

	return exist, nil
}

// DeleteFiles 批量删除对象,对比入参与已删除列表,未删的收集为 DeleteFailure。
func (*AliyunOSS) DeleteFiles(ctx context.Context, keys []string) ([]DeleteFailure, error) {
	if len(keys) == 0 {
		return nil, nil
	}

	bucket, err := NewBucket()
	if err != nil {
		logger.WithCtx(ctx).Mod("upload").Err(err).Error("function AliyunOSS.NewBucket() Failed")
		return nil, errors.New("function AliyunOSS.NewBucket() Failed, err:" + err.Error())
	}

	result, err := bucket.DeleteObjects(keys)
	if err != nil {
		logger.WithCtx(ctx).Mod("upload").Err(err).Error("function bucket.DeleteObjects() failed")
		return nil, errors.New("function bucket.DeleteObjects() failed, err:" + err.Error())
	}

	// 默认非 quiet 模式,OSS 会返回已删除的 key 列表,与之对比找出未删项。
	deleted := make(map[string]struct{}, len(result.DeletedObjects))
	for _, k := range result.DeletedObjects {
		deleted[k] = struct{}{}
	}

	failed := make([]DeleteFailure, 0)
	for _, key := range keys {
		if _, ok := deleted[key]; !ok {
			failed = append(failed, DeleteFailure{Key: key, Err: errors.New("对象未被删除")})
		}
	}
	return failed, nil
}

// ListFiles 按前缀列举对象,cursor 映射为 OSS 的 marker。

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Chunk keys into batches of ≤1000 before calling DeleteObjects
  2. Grant the RAM user oss:DeleteObject on the bucket resources
  3. Read the wrapped err (oss.ServiceError) for the exact code: AccessDenied → permissions, SignatureDoesNotMatch → wrong secret, RequestTimeout → network
  4. Retry transient network errors with backoff; OSS batch delete is idempotent for already-deleted keys

Example fix

// before
result, err := bucket.DeleteObjects(keys)
if err != nil {
	return nil, errors.New("function bucket.DeleteObjects() failed, err:" + err.Error())
}
// after: chunk to OSS limit of 1000 keys per call
const maxBatch = 1000
for i := 0; i < len(keys); i += maxBatch {
	end := i + maxBatch
	if end > len(keys) {
		end = len(keys)
	}
	if _, err := bucket.DeleteObjects(keys[i:end]); err != nil {
		return nil, fmt.Errorf("function bucket.DeleteObjects() failed: %w", err)
	}
}
Defensive patterns

Strategy: retry

Validate before calling

func chunkForOSS(keys []string, size int) [][]string {
	if size <= 0 || size > 1000 {
		size = 1000
	}
	var batches [][]string
	for i := 0; i < len(keys); i += size {
		end := i + size
		if end > len(keys) {
			end = len(keys)
		}
		batches = append(batches, keys[i:end])
	}
	return batches
}

Type guard

func isRetryableOSSErr(err error) bool {
	var se oss.ServiceError
	if !errors.As(err, &se) {
		return true // network-level errors are usually retryable
	}
	return se.StatusCode == 429 || se.StatusCode >= 500 || se.Code == "RequestTimeout"
}

Try / catch

for _, batch := range chunkForOSS(keys, 1000) {
	err := retryWithBackoff(3, func() error {
		_, err := bucket.DeleteObjects(batch)
		return err
	})
	if err != nil {
		if se, ok := isOSSServiceError(err); ok && se.StatusCode == 403 {
			return fmt.Errorf("permission denied deleting objects (check oss:DeleteObject): %w", err)
		}
		return fmt.Errorf("batch delete failed: %w", err)
	}
}

Prevention

When it happens

Trigger: Calling DeleteFiles with >1000 keys (OSS hard limit per DeleteMultipleObjects call), RAM user without oss:DeleteObject permission, expired/invalid credentials, network failure mid-request, or keys with invalid characters that violate OSS key constraints.

Common situations: Bulk cleanup jobs deleting thousands of objects in one call, restricted IAM policies granting only read/upload, key rotation between request signing and execution, batch sizes not chunked before reaching this wrapper.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/92f99c9d4d67ee74. Report an issue: GitHub.