dagger/dagger · error

failed to get OCI state for container %s: %w

Error message

failed to get OCI state for container %s: %w

What it means

After loading the container, getContainerPID calls container.OCIState() to read the runtime state (including the init process PID). This error means reading the OCI state failed — the container's state.json may be unreadable, the process metadata (init pid fd / cgroup) may have vanished, or the container is mid-teardown.

Source

Thrown at engine/engineutil/linux_namespace.go:436

// ShutdownGlobalNamespaceWorkerPool gracefully shuts down the global namespace worker pool
// This should be called during application shutdown
func ShutdownGlobalNamespaceWorkerPool() {
	if globalNSWorkerPool != nil {
		globalNSWorkerPool.Stop()
	}
}

// getContainerPID retrieves the PID of a container using libcontainer
func getContainerPID(containerID string) (int, error) {
	// Load the container using libcontainer
	container, err := libcontainer.Load("/run/runc", containerID)
	if err != nil {
		return 0, fmt.Errorf("failed to create libcontainer factory: %w", err)
	}

	state, err := container.OCIState()
	if err != nil {
		return 0, fmt.Errorf("failed to get OCI state for container %s: %w", containerID, err)
	}

	if state.Pid == 0 {
		return 0, fmt.Errorf("container %s has no running process", containerID)
	}

	return state.Pid, nil
}

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Check the container is in "running" state (runc state <id>) before querying
  2. Handle the exit race: retry the lookup or treat it as container-exited
  3. Ensure the libcontainer library version matches the runc that created the state directory
  4. Read the PID from /run/runc/<id>/state.json directly as a fallback

Example fix

// before
state, err := container.OCIState()
if err != nil { return 0, err }
// after
state, err := container.OCIState()
if err != nil {
    if st, statErr := container.State(); statErr == nil && st.Status == libcontainer.Stopped {
        return 0, fmt.Errorf("container %s already stopped: %w", containerID, err)
    }
    return 0, err
}
Defensive patterns

Strategy: retry

Validate before calling

st, err := container.State()
if err == nil && st.Status != libcontainer.Running {
    return fmt.Errorf("container not running (status=%s); skip PID lookup", st.Status)
}

Try / catch

pid, err := getContainerPID(containerID)
if err != nil && strings.Contains(err.Error(), "failed to get OCI state") {
    time.Sleep(50 * time.Millisecond) // tolerate exit race
    pid, err = getContainerPID(containerID)
}

Prevention

When it happens

Trigger: container.OCIState() returns an error, typically when the container is in a transient state (created/stopped), its init process already exited, or /run/runc/<id>/state.json is stale.

Common situations: Race with container shutdown: runc state exists but the process is gone so pidfd/stat lookups fail; corrupted state file; version mismatch between libcontainer library and the runc that created the state.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/fbf5ba99db8474b1. Report an issue: GitHub.