vxcontrol/pentagi · error

failed to attach list exec for

Error message

failed to attach list exec for '%s': %w

What it means

After the list exec is created, ListContainerDir attaches to its stdio stream via ContainerExecAttach. This error wraps a failure of that attach call — the exec's output stream could not be connected. The exec may have been created but already finished, been removed, or the daemon connection dropped.

Solutions

  1. Retry the whole ListContainerDir call once — the exec may have raced a container shutdown
  2. Confirm the container was still running at the time of the call and re-run on a live container
  3. Check Docker daemon health/socket connectivity (`docker ps`, DOCKER_HOST config)
  4. If using a remote daemon, verify network stability and keep-alive settings
Defensive patterns

Strategy: retry

Validate before calling

state, err := dc.ContainerInspect(ctx, containerID)
if err != nil || !state.State.Running {
    return fmt.Errorf("container gone before attach")
}

Try / catch

listing, err := client.ListContainerDir(ctx, containerID, dir)
if err != nil && strings.Contains(err.Error(), "failed to attach list exec") {
    // transient: retry once with a fresh exec
    listing, err = client.ListContainerDir(ctx, containerID, dir)
}

Prevention

When it happens

Trigger: ContainerExecAttach(ctx, createResp.ID, ...) fails because the exec instance no longer exists (container stopped between create and attach), the Docker daemon connection broke, or the API returned 404/500 for the exec ID.

Common situations: Race where the container is killed right after exec creation; Docker daemon restart or socket drop; network instability against a remote Docker (tcp://) endpoint; long-lived clients hitting an idle-connection timeout.

Related errors


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

Appendix: source

Thrown at backend/pkg/docker/client.go:998

	// TTY GNU coreutils shell-quotes names and busybox wraps them in ANSI escapes,
	// so a readable file with a space / quote / non-ASCII byte would be mis-stat'd
	// and reported unreadable. `find -print0` emits literal bytes and is portable
	// (GNU + busybox); the NUL delimiter also survives names containing newlines.
	// No TTY: a TTY's onlcr would rewrite every \n in the stream to \r\n —
	// including a \n that is part of a filename — corrupting the name. Without a
	// TTY the exec stream is multiplexed and demuxed below.
	createResp, err := dc.ContainerExecCreate(ctx, containerID, client.ExecCreateOptions{
		Cmd:          []string{"find", dirPath, "-maxdepth", "1", "-mindepth", "1", "!", "-name", ".*", "-print0"},
		AttachStdout: true,
		AttachStderr: true,
	})
	if err != nil {
		return ContainerDirListing{}, fmt.Errorf("failed to create list exec for '%s': %w", dirPath, err)
	}

	resp, err := dc.ContainerExecAttach(ctx, createResp.ID, client.ExecAttachOptions{})
	if err != nil {
		return ContainerDirListing{}, fmt.Errorf("failed to attach list exec for '%s': %w", dirPath, err)
	}
	output, readErr := demuxExecStdout(resp.Reader, maxListStdoutBytes)
	resp.Close()
	if readErr != nil {
		return ContainerDirListing{}, fmt.Errorf("failed to read list output for '%s': %w", dirPath, readErr)
	}

	inspect, err := dc.ContainerExecInspect(ctx, createResp.ID)
	if err != nil {
		return ContainerDirListing{}, fmt.Errorf("failed to inspect list exec for '%s': %w", dirPath, err)
	}
	if inspect.ExitCode != 0 {
		return ContainerDirListing{}, fmt.Errorf("list command failed for '%s' with exit code %d: %s", dirPath, inspect.ExitCode, string(output))
	}

	entryPaths, truncated := parseFindEntries(output)

	stats, failures := statContainerEntries(ctx, entryPaths, containerListWorkers, func(ctx context.Context, entryPath string) (container.PathStat, error) {

View on GitHub (pinned to ea665308ba)