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

  1. 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)
  2. Read the wrapped cause: 404 means the container was removed (recreate the flow's container); connection refused means the daemon is down (restart dockerd)
  3. If remote, check DOCKER_HOST connectivity and TLS certs; retry with backoff for transient daemon restarts
  4. 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

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


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/4bc5365867da624d. Report an issue: GitHub.