wtfutil/wtf · error

could not get container list: %w

Error message

could not get container list: %w

What it means

getContainerStates calls cli.ContainerList(ctx, container.ListOptions{All: true}); any transport or API failure is stringified as "could not get container list" into the widget buffer. The daemon's /containers/json call did not return a usable list.

Source

Thrown at modules/docker/client.go:113

	padSlice(true, sysInfo, func(i int) string {
		return sysInfo[i].name
	}, func(i int, newVal string) {
		sysInfo[i].name = newVal
	})

	result := ""
	for _, info := range sysInfo {
		result += fmt.Sprintf("[%s]%s %s\n", widget.settings.labelColor, info.name, info.value)
	}

	return result
}

func (widget *Widget) getContainerStates() string {
	cntrs, err := widget.cli.ContainerList(context.Background(), container.ListOptions{All: true})
	if err != nil {
		return fmt.Errorf("could not get container list: %w", err).Error()
	}

	if len(cntrs) == 0 {
		return " no containers"
	}

	colorMap := map[string]string{
		"created":    "green",
		"running":    "lime",
		"paused":     "yellow",
		"restarting": "yellow",
		"removing":   "yellow",
		"exited":     "red",
		"dead":       "red",
	}

	containers := []struct {
		name  string

View on GitHub (pinned to bb838c1ccb)

Solutions

  1. Confirm docker ps -a works in the same environment.
  2. Recreate the Docker client (NewClientWithOpts) if the daemon restarted — cached connections can go stale.
  3. Fix socket permissions / docker group membership.
  4. Align client library and daemon versions if the error persists across calls.

Example fix

// before
cntrs, err := widget.cli.ContainerList(context.Background(), container.ListOptions{All: true})
if err != nil {
	return fmt.Errorf("could not get container list: %w", err).Error()
}
// after
cntrs, err := widget.cli.ContainerList(context.Background(), container.ListOptions{All: true})
if err != nil {
	return fmt.Errorf("could not get container list: %w", err).Error() // consider rebuilding cli on connection errors
}
Defensive patterns

Strategy: fallback

Validate before calling

if _, err := widget.cli.Ping(context.Background()); err != nil {
	// daemon unreachable — skip ContainerList and show connectivity hint
}

Type guard

func isConnectionReset(err error) bool {
	return errors.Is(err, io.EOF) || errors.Is(err, syscall.ECONNRESET) || errors.Is(err, context.DeadlineExceeded)
}

Try / catch

cntrs, err := widget.cli.ContainerList(ctx, container.ListOptions{All: true})
if err != nil {
	if isConnectionReset(err) {
		widget.reconnect() // rebuild client after daemon restart
	}
	widget.displayBuffer = "container list unavailable: " + err.Error()
	return
}

Prevention

When it happens

Trigger: cli.ContainerList fails: daemon unreachable (same causes as Info), permission denied on the socket, stale connection after daemon restart, or an invalid All option on a mismatched API version.

Common situations: Docker Desktop sleeping/suspending on laptops, socket permissions after a fresh install, remote daemon behind an SSH tunnel that dropped, API version skew after a Docker upgrade.

Related errors


AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03). Data as JSON: /api/errors/c471a39491676b8c. Report an issue: GitHub.