grpc/grpc-go · error

meshca: unsupported config type: %T

Error message

meshca: unsupported config type: %T

What it means

Returned by the pem-file certprovider pluginBuilder.ParseConfig (builder.go:47) when the config value passed in is not a json.RawMessage. Note the message prefix says "meshca" — this is a copy-paste artifact in the pemfile package source; the error actually originates in the file_watcher (PEM file) provider, not MeshCA. The certprovider registry dispatches parsed config as json.RawMessage, so a non-JSON type indicates an internal/registry misuse rather than user input.

Source

Thrown at credentials/tls/certprovider/pemfile/builder.go:47

	"google.golang.org/protobuf/types/known/durationpb"
)

const (
	// PluginName is the name of the PEM file watcher plugin.
	PluginName             = "file_watcher"
	defaultRefreshInterval = 10 * time.Minute
)

func init() {
	certprovider.Register(&pluginBuilder{})
}

type pluginBuilder struct{}

func (p *pluginBuilder) ParseConfig(c any) (*certprovider.BuildableConfig, error) {
	data, ok := c.(json.RawMessage)
	if !ok {
		return nil, fmt.Errorf("meshca: unsupported config type: %T", c)
	}
	opts, err := pluginConfigFromJSON(data)
	if err != nil {
		return nil, err
	}
	return certprovider.NewBuildableConfig(PluginName, opts.canonical(), func(certprovider.BuildOptions) certprovider.Provider {
		return newProvider(opts)
	}), nil
}

func (p *pluginBuilder) Name() string {
	return PluginName
}

func pluginConfigFromJSON(jd json.RawMessage) (Options, error) {
	// The only difference between this anonymous struct and the Options struct
	// is that the refresh_interval is represented here as a duration proto,
	// while in the latter a time.Duration is used.

View on GitHub (pinned to 03255a9237)

Solutions

  1. Always feed config to the certprovider registry as json.RawMessage (json.Marshal your struct first).
  2. Use certprovider.GetProvider / the generated BuildableConfig path rather than calling ParseConfig directly with non-JSON types.
  3. If you hit this from xDS, update the xDS bootstrap and grpc-go to matching versions so the registry hands the expected type.
  4. Report the misleading 'meshca' prefix as a doc/message bug upstream if it is causing confusion.

Example fix

// before
cfg := map[string]string{"certificate_file": "/etc/certs/cert.pem"}
bc, err := builder.ParseConfig(cfg) // wrong type -> meshca: unsupported config type: map[string]string

// after
raw, _ := json.Marshal(cfg)
bc, err := builder.ParseConfig(json.RawMessage(raw)) // json.RawMessage accepted
Defensive patterns

Strategy: validation

Validate before calling

raw, err := json.Marshal(cfgMap)
if err != nil { return err }
bc, err := builder.ParseConfig(json.RawMessage(raw)) // pass json.RawMessage, not the map
if err != nil { return err }

Type guard

func isRawMessage(c any) bool {
    _, ok := c.(json.RawMessage)
    return ok
}

Try / catch

bc, err := builder.ParseConfig(cfg)
if err != nil {
    if strings.Contains(err.Error(), "unsupported config type") {
        // registry gave a non-JSON type; json.Marshal your config first,
        // or update xDS bootstrap + grpc-go to matching versions
    }
    return err
}

Prevention

When it happens

Trigger: Programmatic registration or ParseConfig invocation passing a non-json.RawMessage value (a string, map, or struct) to the file_watcher pluginBuilder; an xDS-bootstrap code path or custom provider glue that bypassed JSON marshalling before calling ParseConfig.

Common situations: Custom code that constructs certprovider plugins directly instead of going through certprovider.BuildableConfig / NewBuildableConfig; an internal/xDS version skew where the registry hands the wrong type; tests that call ParseConfig with a raw map.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/27caf130b78d6384. Report an issue: GitHub.