containerd/containerd · error

failed to marshal TOML blob for runtime %q: %w

Error message

failed to marshal TOML blob for runtime %q: %w

What it means

When decoding runtime options, the config marshals the raw TOML options table (r.Options) back to bytes with toml.Marshal, keyed by r.Type in the error message. If marshaling the in-memory options blob fails, this wrapped error is returned. This happens before unmarshaling into the concrete runtime-options type, so it indicates the stored options structure itself could not be re-encoded as TOML.

Source

Thrown at internal/cri/config/config.go:792

	namespaceOptions := securityContext.GetNamespaceOptions()
	if namespaceOptions.GetNetwork() == runtime.NamespaceMode_NODE ||
		namespaceOptions.GetPid() == runtime.NamespaceMode_NODE ||
		namespaceOptions.GetIpc() == runtime.NamespaceMode_NODE {
		return true
	}

	return false
}

// GenerateRuntimeOptions generates runtime options from cri plugin config.
func GenerateRuntimeOptions(r Runtime) (any, error) {
	if r.Options == nil {
		return nil, nil
	}

	b, err := toml.Marshal(r.Options)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal TOML blob for runtime %q: %w", r.Type, err)
	}

	options := getRuntimeOptionsType(r.Type)
	if err := toml.Unmarshal(b, options); err != nil {
		return nil, err
	}

	// For generic configuration, if no config path specified (preserving old behavior), pass
	// the whole TOML configuration section to the runtime.
	if runtimeOpts, ok := options.(*runtimeoptions.Options); ok && runtimeOpts.ConfigPath == "" {
		runtimeOpts.ConfigBody = b
	}

	return options, nil
}

// getRuntimeOptionsType gets empty runtime options by the runtime type name.
func getRuntimeOptionsType(t string) any {

View on GitHub (pinned to 4246446a2b)

Solutions

  1. Inspect and simplify the options table for the runtime named in the error in config.toml; use only scalar/string/bool/nested-table values.
  2. Regenerate the runtime options using the documented schema for the runtime_type (e.g. SystemdCgroup for runc).
  3. Upgrade/align containerd and the runtime so option marshaling matches expected types.
  4. If constructing Config in Go, ensure Options is a map[string]interface{} of TOML-encodable values.

Example fix

// before (config.toml)
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = "true"  # odd type from template

// after (config.toml)
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true
Defensive patterns

Strategy: validation

Validate before calling

_, err := toml.Marshal(runtime.Options)
if err != nil {
    return fmt.Errorf("runtime %q options not TOML-encodable: %w", runtime.Type, err)
}

Type guard

func tomlEncodable(v map[string]interface{}) bool {
    _, err := toml.Marshal(v)
    return err == nil
}

Try / catch

b, err := toml.Marshal(r.Options)
if err != nil {
    return nil, fmt.Errorf("runtime %q: %w", r.Type, err)
}

Prevention

When it happens

Trigger: Call getRuntimeOptionsBlock for a runtime whose Options map contains values that the TOML marshaller cannot encode (unsupported nested types produced via config decoding or TOML edge cases), during containerd startup when loading runtime configs.

Common situations: Unusual/exotic values in the `[containerd.runtimes.X.options]` table of config.toml, plugin version changes altering how options are represented, or programmatically-built Config objects with non-encodable option values.

Related errors


AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02). Data as JSON: /api/errors/0b3b4a277d9af28d. Report an issue: GitHub.