vxcontrol/pentagi · error
container runtime check failed: %w
Error message
container runtime check failed: %w
What it means
writeFileToContainer begins by calling dockerClient.IsContainerRunning(ctx, t.containerLID) as a preconditions check. If that Docker API call itself returns an error (as opposed to reporting not-running), the error is wrapped with this message. This is a daemon/transport-level failure — the platform could not even ask Docker about the container.
Source
Thrown at backend/pkg/tools/terminal.go:455
successMsg := fmt.Sprintf("File successfully saved to %s", path)
styledMsg := fmt.Sprintf("%s%s%s%s", ansiColorSystemMsg, successMsg, ansiColorReset, ansiLineTerminator)
_, err := t.tlp.PutMsg(ctx, database.TermlogTypeStdin, styledMsg, t.containerID, t.taskID, t.subtaskID)
if err != nil {
return "", fmt.Errorf("failed to put terminal log (write file cmd): %w", err)
}
return fmt.Sprintf("Successfully wrote %d bytes to %s", len(content), path), nil
}
// writeFileToContainer copies content into the flow's container at path,
// overwriting it. It performs no terminal-log writes; WriteFile and EditFile
// each log their own, differently-worded, success message.
func (t *terminal) writeFileToContainer(ctx context.Context, flowID int64, path, content string) error {
containerName := PrimaryTerminalName(t.tenantPrefix, flowID)
isRunning, err := t.dockerClient.IsContainerRunning(ctx, t.containerLID)
if err != nil {
return fmt.Errorf("container runtime check failed: %w", err)
}
if !isRunning {
return fmt.Errorf("target container is not operational")
}
// Docker SDK requires TAR format for file transfer
tarBuffer := &bytes.Buffer{}
archiveWriter := tar.NewWriter(tarBuffer)
defer archiveWriter.Close()
filename := filepath.Base(path)
fileDescriptor := &tar.Header{
Name: filename,
Mode: 0600,
Size: int64(len(content)),
}
err = archiveWriter.WriteHeader(fileDescriptor)
if err != nil {View on GitHub (pinned to ea665308ba)
Solutions
- Verify Docker access from the backend: `docker info` inside the backend container / check the socket mount and group permissions (998:998 or :/var/run/docker.sock)
- Read the wrapped cause: 404 means the container was removed (recreate the flow's container); connection refused means the daemon is down (restart dockerd)
- If remote, check DOCKER_HOST connectivity and TLS certs; retry with backoff for transient daemon restarts
- Ensure the platform recreates the terminal container if the lifecycle manager removed it
Example fix
// before
isRunning, err := t.dockerClient.IsContainerRunning(ctx, t.containerLID)
if err != nil {
return fmt.Errorf("container runtime check failed: %w", err)
}
// after (caller-side retry)
var lastErr error
for i := 0; i < 3; i++ {
running, err := dockerClient.IsContainerRunning(ctx, containerLID)
if err == nil {
break // proceed with `running`
}
lastErr = err
time.Sleep(time.Duration(1<<i) * time.Second)
}
if lastErr != nil {
return fmt.Errorf("container runtime check failed after retries: %w", lastErr)
} Defensive patterns
Strategy: retry
Validate before calling
if _, err := dockerClient.Ping(ctx); err != nil {
return fmt.Errorf("docker daemon unreachable, check socket/DOCKER_HOST: %w", err)
} Type guard
func isDaemonUnavailable(err error) bool {
return errors.Is(err, syscall.ECONNREFUSED) || errors.Is(err, syscall.ENOENT) || errors.Is(err, context.DeadlineExceeded)
} Try / catch
_, err := tool.WriteFile(ctx, flowID, content, path)
if err != nil && strings.Contains(err.Error(), "container runtime check failed") {
// check dockerd, socket mount, or DOCKER_HOST, then retry with backoff
return fmt.Errorf("docker unavailable: %w", err)
} Prevention
- Mount /var/run/docker.sock (or configure DOCKER_HOST) into the backend and verify at startup
- Health-check the Docker daemon before starting flows
- Retry IsContainerRunning with exponential backoff for transient daemon restarts
- Alert on 404s for known container IDs — the container was removed out-of-band
When it happens
Trigger: Docker daemon unreachable (socket permission denied, daemon restarting, /var/run/docker.sock not mounted), Docker API timeout, the containerLID no longer resolves to a live container object returning a 404, or a remote DOCKER_HOST connection dropped.
Common situations: Deploying without the Docker socket mounted into the backend container; `dockerd` crash/restart mid-flow; DOCKER_HOST over TCP with network/TLS misconfig; container was force-removed (`docker rm -f`) while the flow still holds its ID.
Related errors
- failed to initialize docker client: %w
- docker exec systemerr: %s
- failed to list images: %w
- Internal
- failed to stop flow %d: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/4bc5365867da624d.
Report an issue: GitHub.