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
- Check the container is in "running" state (runc state <id>) before querying
- Handle the exit race: retry the lookup or treat it as container-exited
- Ensure the libcontainer library version matches the runc that created the state directory
- 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
- Check container status is "running" before reading OCIState
- Treat OCIState failures during teardown as container-exited, not fatal
- Keep libcontainer library version aligned with installed runc
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
- failed to create libcontainer factory: %w
- expected index, got %s
- read index blob: %w
- unmarshal index: %w
- expected manifest or index, got %s
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/fbf5ba99db8474b1.
Report an issue: GitHub.