{"record":{"id":"2f101e83c802c360","repo":"googleapis/mcp-toolbox","slug":"operation-finished-with-error-but-could-not-marsha-2f101e","errorCode":null,"errorMessage":"operation finished with error but could not marshal error object: %w","messagePattern":"operation finished with error but could not marshal error object: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sources/cloudsqladmin/cloud_sql_admin.go","lineNumber":357,"sourceCode":"\n\treturn resp, nil\n}\n\nfunc (s *Source) GetWaitForOperations(ctx context.Context, service *sqladmin.Service, project, operation, connectionMessageTemplate string, delay time.Duration) (any, error) {\n\tlogger, err := util.LoggerFromContext(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\top, err := service.Operations.Get(project, operation).Do()\n\tif err != nil {\n\t\tlogger.DebugContext(ctx, fmt.Sprintf(\"error getting operation: %s, retrying in %v\", err, delay))\n\t} else {\n\t\tif op.Status == \"DONE\" {\n\t\t\tif op.Error != nil {\n\t\t\t\tvar errorBytes []byte\n\t\t\t\terrorBytes, err = json.Marshal(op.Error)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"operation finished with error but could not marshal error object: %w\", err)\n\t\t\t\t}\n\t\t\t\treturn nil, fmt.Errorf(\"operation finished with error: %s\", string(errorBytes))\n\t\t\t}\n\n\t\t\tvar opBytes []byte\n\t\t\topBytes, err = op.MarshalJSON()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"could not marshal operation: %w\", err)\n\t\t\t}\n\n\t\t\tvar data map[string]any\n\t\t\tif err := json.Unmarshal(opBytes, &data); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"could not unmarshal operation: %w\", err)\n\t\t\t}\n\n\t\t\tif msg, ok := generateCloudSQLConnectionMessage(ctx, s, logger, data, connectionMessageTemplate); ok {\n\t\t\t\treturn msg, nil\n\t\t\t}","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/sources/cloudsqladmin/cloud_sql_admin.go#L339-L375","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Upgrade google.golang.org/api (sqladmin package) to the latest version to fix any marshaling incompatibility","Inspect op.Error fields directly via debugging/logging instead of relying on marshaling","Log the operation name and fetch it with gcloud sql operations describe to see the real failure","Check whether a proxy or modified client is corrupting API responses"],"exampleFix":"// before\nerrorBytes, err = json.Marshal(op.Error)\nif err != nil {\n    return nil, fmt.Errorf(\"operation finished with error but could not marshal error object: %w\", err)\n}\n// after\nif errorBytes, err = json.Marshal(op.Error); err != nil {\n    return nil, fmt.Errorf(\"operation finished with error (status=%s, errors=%v; marshal failed: %w)\", op.Status, op.Error, err)\n}","handlingStrategy":"type-guard","validationCode":"// Nothing meaningful can be validated pre-call; optionally sanity-check the client library version\nimport _ \"google.golang.org/api/sqladmin/v1\"\nfunc checkClientUsable(op *sqladmin.Operation) error {\n    if op == nil {\n        return fmt.Errorf(\"nil operation\")\n    }\n    if _, err := json.Marshal(op.Error); err != nil {\n        return fmt.Errorf(\"pre-check: op.Error is not marshalable: %w\", err)\n    }\n    return nil\n}","typeGuard":"func operationErrorMarshalable(op *sqladmin.Operation) (string, bool) {\n    if op == nil || op.Error == nil {\n        return \"\", false\n    }\n    b, err := json.Marshal(op.Error)\n    if err != nil {\n        return fmt.Sprintf(\"%+v\", op.Error), false\n    }\n    return string(b), true\n}","tryCatchPattern":"result, err := src.GetWaitForOperations(ctx, service, project, opName, tpl, delay)\nif err != nil && strings.Contains(err.Error(), \"could not marshal error object\") {\n    // Fall back to raw API inspection\n    op, _ := service.Operations.Get(project, opName).Do()\n    if op != nil && op.Error != nil {\n        for _, e := range op.Error.Errors {\n            log.Printf(\"operation error: code=%s message=%s\", e.Code, e.Message)\n        }\n    }\n    return\n}","preventionTips":["Keep google.golang.org/api up to date so generated types marshal correctly","Prefer reading structured fields (op.Error.Errors[].Code/Message) over JSON strings for error reporting","Add a fallback in error handling that describes the operation via gcloud sql operations describe"],"tags":["gcp","cloudsql","json-marshal","operations"],"backgroundTag":"json-marshal-failed","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}