googleapis/mcp-toolbox · error

operation finished with error: %s

Error message

operation finished with error: %s

What it means

This error is returned by GetOperations when a polled AlloyDB long-running operation has completed unsuccessfully: op.Done is true and op.Error is non-nil. The Google Rpc.Status object (HTTP status code and error message from the failed operation) is JSON-marshaled and embedded in this message, so the full failure reason is in the error text. It represents the authoritative failure of a create/delete/patch operation, not a client-side problem.

Source

Thrown at internal/sources/alloydbadmin/alloydbadmin.go:351

	service, err := s.getService(ctx, accessToken)
	if err != nil {
		return nil, err
	}

	name := fmt.Sprintf("projects/%s/locations/%s/operations/%s", project, location, operation)

	op, err := service.Projects.Locations.Operations.Get(name).Do()
	if err != nil {
		logger.DebugContext(ctx, fmt.Sprintf("error getting operation: %s, retrying in %v\n", err, delay))
	} else {
		if op.Done {
			if op.Error != nil {
				var errorBytes []byte
				errorBytes, err = json.Marshal(op.Error)
				if err != nil {
					return nil, fmt.Errorf("operation finished with error but could not marshal error object: %w", err)
				}
				return nil, fmt.Errorf("operation finished with error: %s", string(errorBytes))
			}

			var opBytes []byte
			opBytes, err = op.MarshalJSON()
			if err != nil {
				return nil, fmt.Errorf("could not marshal operation: %w", err)
			}

			if op.Response != nil {
				var responseData map[string]any
				if err := json.Unmarshal(op.Response, &responseData); err == nil && responseData != nil {
					if msg, ok := generateAlloyDBConnectionMessage(responseData, connectionMessageTemplate); ok {
						return msg, nil
					}
				}
			}

			var result any

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Parse the JSON payload in the message (fields code and message) to read the API's failure reason
  2. If the code indicates quota (e.g., 8/RESOURCE_EXHAUSTED or 429), check AlloyDB quotas in the Cloud Console and request an increase
  3. If permissions-related, verify the caller's IAM roles include roles/alloydb.admin before re-running the operation
  4. Fix the underlying configuration (machine type, network, region) and resubmit the create/update request
  5. Check Cloud Logging for the operation's detailed server-side logs for root cause

Example fix

// before
if op.Error != nil {
    errorBytes, _ := json.Marshal(op.Error)
    return nil, fmt.Errorf("operation finished with error: %s", string(errorBytes))
}
// after
if op.Error != nil {
    var rpcStatus struct {
        Code    int    `json:"code"`
        Message string `json:"message"`
    }
    if b, err := json.Marshal(op.Error); err == nil && json.Unmarshal(b, &rpcStatus) == nil {
        return nil, fmt.Errorf("operation failed (gRPC code %d): %s", rpcStatus.Code, rpcStatus.Message)
    }
    errorBytes, _ := json.Marshal(op.Error)
    return nil, fmt.Errorf("operation finished with error: %s", string(errorBytes))
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: pre-flight checks before starting/polling an operation
if err := checkQuotaAndBilling(project); err != nil {
    return fmt.Errorf("preflight failed: %w", err)
}
// ensure config (machine type, region, network) is valid before submitting

Type guard

func ParseOperationFailure(err error) (grpcCode int, message string, ok bool) {
    const prefix = "operation finished with error: "
    msg := err.Error()
    i := strings.Index(msg, prefix)
    if i < 0 {
        return 0, "", false
    }
    var st struct {
        Code    int    `json:"code"`
        Message string `json:"message"`
    }
    if json.Unmarshal([]byte(msg[i+len(prefix):]), &st) != nil {
        return 0, "", false
    }
    return st.Code, st.Message, true
}

Try / catch

_, err := s.GetOperations(ctx, project, location, opName, tmpl, pollDelay, accessToken)
if err != nil {
    if code, msg, ok := ParseOperationFailure(err); ok {
        switch code {
        case 8: // RESOURCE_EXHAUSTED
            return fmt.Errorf("quota exceeded: %s — request quota increase", msg)
        case 7: // PERMISSION_DENIED
            return fmt.Errorf("IAM permission missing: %s", msg)
        default:
            return fmt.Errorf("operation failed: %s", msg)
        }
    }
    return err
}

Prevention

When it happens

Trigger: Polling an operation via Projects.Locations.Operations.Get whose target action failed server-side — e.g., instance creation rejected due to quota exhaustion, invalid configuration, permission denial, or resource conflicts — and the API returned Done=true with an Error status.

Common situations: Exceeded AlloyDB quota or regional capacity, invalid instance settings (machine type, network), permission revoked between request start and completion, project billing issues, and conflicts like a duplicate resource created concurrently.

Related errors


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