vxcontrol/pentagi · error

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

Error message

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

What it means

ListContainerDir runs `find <dir> -maxdepth 1 -print0` inside the container via a Docker exec. This error wraps a failure of the ContainerExecCreate API call, meaning the exec process could not even be created in the target container. It is thrown before any output is read, so the listing is entirely unavailable. The wrapped cause (Docker API error) is preserved via %w.

Source

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

	if !dirStat.Mode.IsDir() {
		return ContainerDirListing{}, fmt.Errorf("container path '%s' is not a directory", dirPath)
	}

	// List direct children NUL-delimited. Parsing `ls` output is unsafe: under a
	// 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))

View on GitHub (pinned to ea665308ba)

Solutions

  1. Verify the container is running: `docker inspect -f '{{.State.Running}}' <containerID>` and use the correct ID
  2. Recreate/restart the container and retry the listing
  3. Ensure the image contains a `find` binary (add busybox/coreutils to the Dockerfile)
  4. Check Docker daemon connectivity (`docker ps` from the host running the code)

Example fix

// before: exec fails because container is stopped
listing, err := dc.ListContainerDir(ctx, deadContainerID, "/work")
// after: guard on container state before listing
state, _ := dc.ContainerInspect(ctx, containerID)
if !state.State.Running {
    return fmt.Errorf("container %s not running", containerID)
}
listing, err := dc.ListContainerDir(ctx, containerID, "/work")
Defensive patterns

Strategy: retry

Validate before calling

state, err := dc.ContainerInspect(ctx, containerID)
if err != nil || !state.State.Running {
    return fmt.Errorf("container %s not listable", containerID)
}

Try / catch

listing, err := client.ListContainerDir(ctx, containerID, dir)
if err != nil {
    var derr *dockerUnknownContainer
    if errors.As(err, &derr) { /* container gone — recreate */ }
    return err
}

Prevention

When it happens

Trigger: Calling ListContainerDir(ctx, containerID, dirPath) when the container is stopped/removed, the containerID is wrong, the image lacks a `find` binary (no PATH exec resolution), the Docker daemon is unreachable, or the API request is rejected (404 no such container, 409 container not running).

Common situations: Container exited or was reaped between stat and exec; typo'd or stale container ID; minimal images (distroless, scratch) without coreutils/busybox `find`; Docker daemon restarted or socket permissions changed mid-flow.

Related errors


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