googleapis/mcp-toolbox · error

failed to marshal to JSON: %w

Error message

failed to marshal to JSON: %w

What it means

unmarshalProto first json.Marshal's the generic config value (parsed from YAML) before feeding it to protojson.Unmarshal. This error means the marshal step itself failed, which is rare and indicates the value contains a type the standard JSON encoder cannot serialize (e.g. channel, func, or a malformed structure) rather than a schema mismatch.

Source

Thrown at internal/tools/serverlessspark/createbatch/config.go:33

package createbatch

import (
	"context"
	"encoding/json"
	"fmt"

	dataprocpb "cloud.google.com/go/dataproc/v2/apiv1/dataprocpb"
	"github.com/goccy/go-yaml"
	"github.com/googleapis/mcp-toolbox/internal/tools"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
)

// unmarshalProto is a helper function to unmarshal a generic interface{} into a proto.Message.
func unmarshalProto(data any, m proto.Message) error {
	jsonData, err := json.Marshal(data)
	if err != nil {
		return fmt.Errorf("failed to marshal to JSON: %w", err)
	}
	return protojson.Unmarshal(jsonData, m)
}

type compatibleSource interface {
	CreateBatch(context.Context, *dataprocpb.Batch) (map[string]any, error)
}

// Config is a common config that can be used with any type of create batch tool. However, each tool
// will still need its own config type, embedding this Config, so it can provide a type-specific
// Initialize implementation.
type Config struct {
	tools.ConfigBase  `yaml:",inline"`
	Type              string                        `yaml:"type" validate:"required"`
	Source            string                        `yaml:"source" validate:"required"`
	RuntimeConfig     *dataprocpb.RuntimeConfig     `yaml:"runtimeConfig"`
	EnvironmentConfig *dataprocpb.EnvironmentConfig `yaml:"environmentConfig"`
}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Inspect the wrapped error after 'failed to marshal to JSON:' to see the offending type
  2. Ensure RuntimeConfig/EnvironmentConfig values come from standard YAML/JSON decoding (maps, slices, scalars)
  3. If building Config in Go, use JSON-serializable structures or protojson-native types

Example fix

// before
rc := map[string]any{"properties": make(chan int)}
cfg.RuntimeConfig, _ = unmarshalToProto(rc)
// after
rc := map[string]any{"properties": map[string]any{"key": "value"}}
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the value is JSON-encodable before handing to NewConfig
if _, err := json.Marshal(runtimeConfigRaw); err != nil {
    return fmt.Errorf("runtimeConfig is not JSON-encodable: %w", err)
}

Type guard

func isJSONEncodable(v any) bool { _, err := json.Marshal(v); return err == nil }

Try / catch

cfg, err := NewConfig(ctx, raw)
if err != nil && strings.Contains(err.Error(), "failed to marshal to JSON") {
    log.Fatalf("runtimeConfig/environmentConfig contains non-serializable data: %v", err)
}

Prevention

When it happens

Trigger: NewConfig calls unmarshalProto with a decoded YAML value for RuntimeConfig or EnvironmentConfig whose underlying data cannot be marshaled by encoding/json (e.g. contains non-serializable Go values injected programmatically).

Common situations: Constructing Config programmatically (not via YAML) and passing unsupported types in RuntimeConfig/EnvironmentConfig; custom YAML decoders producing exotic types.

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