flipped-aurora/gin-vue-admin · error

obs delete %s failed: code=%s, message=%s

Error message

obs delete %s failed: code=%s, message=%s

What it means

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.

Source

Thrown at server/utils/upload/obs.go:134

	for _, k := range keys {
		objects = append(objects, obs.ObjectToDelete{Key: k})
	}

	output, err := client.DeleteObjects(&obs.DeleteObjectsInput{
		Bucket:  global.GVA_CONFIG.HuaWeiObs.Bucket,
		Objects: objects,
		Quiet:   true,
	})
	if err != nil {
		logger.WithCtx(ctx).Mod("upload").Err(err).Error("obs DeleteObjects 失败")
		return nil, err
	}

	if output != nil {
		for _, e := range output.Errors {
			failed = append(failed, DeleteFailure{
				Key: e.Key,
				Err: fmt.Errorf("obs delete %s failed: code=%s, message=%s", e.Key, e.Code, e.Message),
			})
		}
	}
	return failed, nil
}

// ListFiles 按前缀列举对象,marker 分页:Marker=cursor,NextMarker→nextCursor,IsTruncated→hasMore。
func (o *Obs) ListFiles(ctx context.Context, prefix, cursor string, limit int) (files []FileInfo, nextCursor string, hasMore bool, err error) {
	if limit <= 0 {
		limit = 100
	}

	client, err := NewHuaWeiObsClient()
	if err != nil {
		logger.WithCtx(ctx).Mod("upload").Err(err).Error("获取华为对象存储对象失败")
		return nil, "", false, pkgerrors.Wrap(err, "获取华为对象存储对象失败!")
	}

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Read the code= and message= fields per DeleteFailure to identify the OBS error and the affected key.
  2. Verify the access key has OBS delete permission (IAM policy) for the bucket.
  3. Retry deleting only the failed keys after fixing permissions or confirming the objects' existence.
  4. Handle per-key failures individually — other keys in the batch may have succeeded.

Example fix

// before
failed, _ := uploader.DeleteFiles(ctx, keys) // ignores per-key failures

// after
failed, err := uploader.DeleteFiles(ctx, keys)
for _, f := range failed {
    log.Printf("retry delete %s: %v", f.Key, f.Err)
    _ = retryDelete(ctx, uploader, f.Key)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// best effort: check keys exist / are deletable before batch delete
for _, key := range keys {
    if key == "" || strings.HasPrefix(key, "/") {
        return fmt.Errorf("invalid key for OBS delete: %q", key)
    }
}

Try / catch

failed, err := uploader.DeleteFiles(ctx, keys)
if err != nil {
    return err
}
for _, f := range failed {
    var delErr *upload.DeleteFailure
    log.Printf("obs delete failed key=%s: %v", f.Key, f.Err)
    // inspect code= in f.Err to decide retry vs skip
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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