googleapis/mcp-toolbox · error

could not marshal operation: %w

Error message

could not marshal operation: %w

What it means

After a successful DONE operation, the library calls op.MarshalJSON() to serialize the full operation object for the response. If that built-in marshaling fails, this error is thrown. In practice this is near-impossible with stock sqladmin.Operation types unless the API client library is mismatched or the object holds unusual ForceSendFields/NullFields state.

Source

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

	}
	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
			}
			return string(opBytes), nil
		}
		logger.DebugContext(ctx, fmt.Sprintf("operation not complete, retrying in %v", delay))
	}
	return nil, nil
}

func (s *Source) InsertBackupRun(ctx context.Context, project, instance, location, backupDescription, accessToken string) (any, error) {

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Upgrade the google.golang.org/api dependency to the latest version
  2. Fall back to json.Marshal(op) in the handler to capture and inspect the failure
  3. Log the operation name and retrieve it via gcloud sql operations describe instead of in-process marshaling
  4. Pin consistent versions of google.golang.org/api across the build to avoid generated-type mismatches

Example fix

// before
opBytes, err = op.MarshalJSON()
if err != nil {
    return nil, fmt.Errorf("could not marshal operation: %w", err)
}
// after
opBytes, err = op.MarshalJSON()
if err != nil {
    logger.WarnContext(ctx, fmt.Sprintf("op.MarshalJSON failed (%v), falling back to json.Marshal", err))
    if opBytes, err = json.Marshal(op); err != nil {
        return nil, fmt.Errorf("could not marshal operation %s: %w", op.Name, err)
    }
}
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-check that the operation object serializes cleanly right after fetch
func operationSerializable(op *sqladmin.Operation) error {
    if _, err := op.MarshalJSON(); err != nil {
        return fmt.Errorf("operation %s not serializable: %w", op.Name, err)
    }
    return nil
}

Type guard

func canMarshalOperation(op *sqladmin.Operation) ([]byte, bool) {
    b, err := op.MarshalJSON()
    if err != nil {
        if b2, err2 := json.Marshal(op); err2 == nil {
            return b2, true
        }
        return nil, false
    }
    return b, true
}

Try / catch

result, err := src.GetWaitForOperations(ctx, service, project, opName, tpl, delay)
if err != nil && strings.Contains(err.Error(), "could not marshal operation") {
    // fallback: fetch and describe via CLI or raw HTTP
    out, cmdErr := exec.Command("gcloud", "sql", "operations", "describe", opName, "--project", project, "--format=json").Output()
    if cmdErr == nil {
        fmt.Println(string(out))
    }
    return
}

Prevention

When it happens

Trigger: service.Operations.Get returned a DONE operation with no op.Error, but op.MarshalJSON() returned a non-nil error during result serialization in GetWaitForOperations.

Common situations: Version incompatibility between the generated sqladmin API types and the serialization helpers, corrupted client state, or unusual operation payloads from newer API versions not understood by an older google.golang.org/api version.

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/d1c610993f5e5a10. Report an issue: GitHub.