containerd/containerd · error

can't load base OCI spec %q: %w

Error message

can't load base OCI spec %q: %w

What it means

When a base OCI spec file is configured (base_spec_file in the CRI runtime config), runtimeSpec loads it via c.LoadOCISpec and wraps any load failure with this error. It means the base runtime spec JSON could not be read or parsed into an oci.Spec.

Source

Thrown at internal/cri/server/container_create.go:519

			HostPath:       src,
			SelinuxRelabel: true,
			UidMappings:    uidMappings,
			GidMappings:    gidMappings,
		})
	}
	return mounts
}

// runtimeSpec returns a default runtime spec used in cri-containerd.
func (c *criService) runtimeSpec(id string, platform imagespec.Platform, baseSpecFile string, opts ...oci.SpecOpts) (*runtimespec.Spec, error) {
	// GenerateSpec needs namespace.
	ctx := util.NamespacedContext()
	container := &containers.Container{ID: id}

	if baseSpecFile != "" {
		baseSpec, err := c.LoadOCISpec(baseSpecFile)
		if err != nil {
			return nil, fmt.Errorf("can't load base OCI spec %q: %w", baseSpecFile, err)
		}

		spec := oci.Spec{}
		if err := util.DeepCopy(&spec, &baseSpec); err != nil {
			return nil, fmt.Errorf("failed to clone OCI spec: %w", err)
		}

		// Fix up cgroups path
		applyOpts := append([]oci.SpecOpts{oci.WithNamespacedCgroup()}, opts...)

		if err := oci.ApplyOpts(ctx, nil, container, &spec, applyOpts...); err != nil {
			return nil, fmt.Errorf("failed to apply OCI options: %w", err)
		}

		return &spec, nil
	}

	spec, err := oci.GenerateSpecWithPlatform(ctx, nil, platforms.Format(platform), container, opts...)

View on GitHub (pinned to 4246446a2b)

Solutions

  1. Verify the path in containerd config (plugins.'io.containerd.grpc.v1.cri'.containerd.base_spec_file) exists and is readable by containerd
  2. Validate the JSON with `jq . <file>` or `oci-runtime-tool validate --file <file>` and fix syntax/schema errors
  3. Generate a known-good baseline: `runc spec > /etc/containerd/base-spec.json` and point base_spec_file at it
  4. Remove base_spec_file from config to fall back to the default generated spec, then restart containerd

Example fix

// before (config.toml)
[plugins."io.containerd.grpc.v1.cri".containerd]
  base_spec_file = "/etc/containerd/basespec.json"  # file missing
// after
[plugins."io.containerd.grpc.v1.cri".containerd]
  base_spec_file = "/etc/containerd/base-spec.json" # exists, valid JSON
Defensive patterns

Strategy: validation

Validate before calling

// preflight the base spec file before (re)starting containerd
package main
import ("encoding/json", "fmt", "os")
func checkBaseSpec(path string) error {
	b, err := os.ReadFile(path)
	if err != nil { return err }
	var s struct { OCIVersion string `json:"ociVersion"` }
	if err := json.Unmarshal(b, &s); err != nil { return fmt.Errorf("invalid JSON: %w", err) }
	if s.OCIVersion == "" { return fmt.Errorf("missing ociVersion") }
	return nil
}

Try / catch

if err != nil && strings.Contains(err.Error(), "can't load base OCI spec") {
	// fall back: run with default spec (remove base_spec_file) after validating the file
	log.Printf("base spec unusable: %v", err)
}

Prevention

When it happens

Trigger: baseSpecFile is set in the containerd CRI config and LoadOCISpec fails because the file does not exist, is unreadable, or contains invalid JSON/an invalid OCI spec schema.

Common situations: Typo'd or deleted base_spec_file path in containerd config.toml; hand-edited spec JSON with syntax errors; file created after containerd started with wrong permissions; spec generated for an incompatible OCI spec version.

Related errors


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