googleapis/mcp-toolbox · error

failed to delete object %q in bucket %q: %w

Error message

failed to delete object %q in bucket %q: %w

What it means

DeleteObject wraps any error returned by the GCS client's Object.Delete call with the object and bucket names. The library throws it whenever the underlying Cloud Storage API rejects or fails the delete operation; the wrapped cause (e.g. storage.ErrObjectNotExist, 403, preconditions) is preserved via %w.

Source

Thrown at internal/sources/cloudstorage/cloudstorage.go:596

		return nil, fmt.Errorf("failed to move %q to %q in bucket %q: %w", sourceObject, destinationObject, bucket, err)
	}

	return map[string]any{
		"bucket":            bucket,
		"sourceObject":      sourceObject,
		"destinationObject": destinationObject,
		"bytes":             attrs.Size,
		"contentType":       attrs.ContentType,
	}, nil
}

// DeleteObject deletes a GCS object.
func (s *Source) DeleteObject(ctx context.Context, bucket, object string) (map[string]any, error) {
	if err := s.validateBucket(bucket); err != nil {
		return nil, err
	}
	if err := s.client.Bucket(bucket).Object(object).Delete(ctx); err != nil {
		return nil, fmt.Errorf("failed to delete object %q in bucket %q: %w", object, bucket, err)
	}

	return map[string]any{
		"bucket":  bucket,
		"object":  object,
		"deleted": true,
	}, nil
}

// DeleteBucket deletes an empty Cloud Storage bucket.
func (s *Source) DeleteBucket(ctx context.Context, bucket string) (map[string]any, error) {
	if err := s.validateBucket(bucket); err != nil {
		return nil, err
	}
	if err := s.client.Bucket(bucket).Delete(ctx); err != nil {
		return nil, fmt.Errorf("failed to delete bucket %q: %w", bucket, err)
	}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Check the wrapped cause: errors.Is(err, storage.ErrObjectNotExist) means the object is already gone — treat as success for idempotent cleanup
  2. Verify the credentials/service account has roles/storage.objectAdmin (or storage.objects.delete) on the bucket
  3. Confirm the object name exactly matches (no leading slash, correct encoding)
  4. Check for retention policy, event-based hold, or temporary hold on the object/bucket and lift them before deleting
  5. Retry on transient errors (5xx, context deadline) with backoff

Example fix

// before
_, err := src.DeleteObject(ctx, bucket, object)
if err != nil { return err }
// after
_, err := src.DeleteObject(ctx, bucket, object)
if err != nil {
    if errors.Is(err, storage.ErrObjectNotExist) {
        return nil // already deleted, idempotent
    }
    return fmt.Errorf("delete %s/%s: %w", bucket, object, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if bucket == "" || object == "" { return errors.New("bucket and object are required") }

Try / catch

res, err := src.DeleteObject(ctx, bucket, object)
if err != nil {
    if strings.Contains(err.Error(), "notExist") || errors.Is(err, storage.ErrObjectNotExist) {
        // treat as already deleted
        return nil
    }
    if strings.Contains(err.Error(), "403") {
        // permissions problem: check IAM roles
    }
    return err
}

Prevention

When it happens

Trigger: Calling Source.DeleteObject(ctx, bucket, object) when the object does not exist, the caller lacks storage.objects.delete permission, a generation precondition fails (object was modified concurrently), the bucket name fails validateBucket, or a transient GCS/network error occurs.

Common situations: Deleting an already-removed object (double delete in cleanup jobs), service account missing storage.admin or objectAdmin role, deleting objects with retention/hold policies applied, typos in object names, or bucket configured with retention lock.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/d984a28f5f3dad82. Report an issue: GitHub.