googleapis/mcp-toolbox · error

operation finished with error but could not marshal error ob

Error message

operation finished with error but could not marshal error object: %w

What it means

When a Cloud SQL operation reaches DONE status with a non-nil op.Error, the library attempts to json.Marshal op.Error to include its details in the returned error. If marshaling fails (extremely rare for API-generated OperationErrors, e.g. due to a client/version mismatch producing unmarshalable fields), this wrapper error is thrown instead of the operation's actual error. It indicates the operation failed but its error payload could not be serialized.

Source

Thrown at internal/sources/cloudsqladmin/cloud_sql_admin.go:357

	return resp, nil
}

func (s *Source) GetWaitForOperations(ctx context.Context, service *sqladmin.Service, project, operation, connectionMessageTemplate string, delay time.Duration) (any, error) {
	logger, err := util.LoggerFromContext(ctx)
	if err != nil {
		return nil, err
	}
	op, err := service.Operations.Get(project, operation).Do()
	if err != nil {
		logger.DebugContext(ctx, fmt.Sprintf("error getting operation: %s, retrying in %v", err, delay))
	} else {
		if op.Status == "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)
			}

			var data map[string]any
			if err := json.Unmarshal(opBytes, &data); err != nil {
				return nil, fmt.Errorf("could not unmarshal operation: %w", err)
			}

			if msg, ok := generateCloudSQLConnectionMessage(ctx, s, logger, data, connectionMessageTemplate); ok {
				return msg, nil
			}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Upgrade google.golang.org/api (sqladmin package) to the latest version to fix any marshaling incompatibility
  2. Inspect op.Error fields directly via debugging/logging instead of relying on marshaling
  3. Log the operation name and fetch it with gcloud sql operations describe to see the real failure
  4. Check whether a proxy or modified client is corrupting API responses

Example fix

// before
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)
}
// after
if errorBytes, err = json.Marshal(op.Error); err != nil {
    return nil, fmt.Errorf("operation finished with error (status=%s, errors=%v; marshal failed: %w)", op.Status, op.Error, err)
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Nothing meaningful can be validated pre-call; optionally sanity-check the client library version
import _ "google.golang.org/api/sqladmin/v1"
func checkClientUsable(op *sqladmin.Operation) error {
    if op == nil {
        return fmt.Errorf("nil operation")
    }
    if _, err := json.Marshal(op.Error); err != nil {
        return fmt.Errorf("pre-check: op.Error is not marshalable: %w", err)
    }
    return nil
}

Type guard

func operationErrorMarshalable(op *sqladmin.Operation) (string, bool) {
    if op == nil || op.Error == nil {
        return "", false
    }
    b, err := json.Marshal(op.Error)
    if err != nil {
        return fmt.Sprintf("%+v", op.Error), false
    }
    return string(b), true
}

Try / catch

result, err := src.GetWaitForOperations(ctx, service, project, opName, tpl, delay)
if err != nil && strings.Contains(err.Error(), "could not marshal error object") {
    // Fall back to raw API inspection
    op, _ := service.Operations.Get(project, opName).Do()
    if op != nil && op.Error != nil {
        for _, e := range op.Error.Errors {
            log.Printf("operation error: code=%s message=%s", e.Code, e.Message)
        }
    }
    return
}

Prevention

When it happens

Trigger: service.Operations.Get returned an operation with Status == "DONE" and op.Error != nil, but json.Marshal(op.Error) returned an error — practically only when the sqladmin library's SqladminEmpty/OperationError types cannot serialize (unsupported types, custom marshaling failure).

Common situations: Almost never seen in practice; would require a corrupted or unexpected op.Error payload from the API, a Go google-cloud-go/sqladmin version incompatibility, or monkey-patched/defective JSON marshaling of the operation error type.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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