vxcontrol/pentagi · error

failed to attach to exec process: %w

Error message

failed to attach to exec process: %w

What it means

This error is returned by getExecResult when the Docker API call ContainerExecAttach fails, meaning the library could not attach a TTY stream to the already-created exec instance inside the container. Attaching is required to capture the command's output, so without it the result cannot be read.

Source

Thrown at backend/pkg/tools/terminal.go:273

			return fmt.Sprintf("Command started in background with timeout %s (still running)", timeout), nil
		}
	}

	return t.getExecResult(ctx, createResp.ID, timeout)
}

func (t *terminal) getExecResult(ctx context.Context, id string, timeout time.Duration) (string, error) {
	if timeout > 0 {
		var cancel context.CancelFunc
		ctx, cancel = context.WithTimeout(ctx, timeout)
		defer cancel()
	}

	resp, err := t.dockerClient.ContainerExecAttach(ctx, id, client.ExecAttachOptions{
		TTY: true,
	})
	if err != nil {
		return "", fmt.Errorf("failed to attach to exec process: %w", err)
	}
	defer resp.Close()

	dst := bytes.Buffer{}
	errChan := make(chan error, 1)

	go func() {
		_, copyErr := io.Copy(&dst, resp.Reader)
		errChan <- copyErr
	}()

	select {
	case err := <-errChan:
		if err != nil && err != io.EOF {
			return "", fmt.Errorf("failed to copy output: %w", err)
		}
	case <-ctx.Done():
		// Close the response to unblock io.Copy

View on GitHub (pinned to ea665308ba)

Solutions

  1. Verify the target container is running before executing (docker ps / IsContainerRunning)
  2. Check Docker daemon health and socket permissions (docker info; user in docker group)
  3. Retry the command — attach races are often transient
  4. Confirm the Docker client library and daemon API versions are compatible

Example fix

// before
out, err := term.ExecCommand(ctx, flowID, cmd, false) // container was just restarted
// after
if ok, _ := docker.IsContainerRunning(ctx, containerLID); !ok {
    return errors.New("container not running; start it before ExecCommand")
}
out, err := term.ExecCommand(ctx, flowID, cmd, false)
Defensive patterns

Strategy: retry

Validate before calling

if ok, err := docker.IsContainerRunning(ctx, containerLID); err != nil || !ok {
    return fmt.Errorf("container not ready before exec: running=%v err=%v", ok, err)
}

Try / catch

out, err := term.ExecCommand(ctx, flowID, cmd, false)
if err != nil && strings.Contains(err.Error(), "failed to attach to exec process") {
    time.Sleep(500 * time.Millisecond)
    out, err = term.ExecCommand(ctx, flowID, cmd, false) // single retry
}

Prevention

When it happens

Trigger: ExecCommand → getExecResult calls t.dockerClient.ContainerExecAttach(ctx, id, ExecAttachOptions{TTY:true}) and the Docker daemon returns an error: exec instance was removed, container stopped between create and attach, daemon connection lost, or the exec ID is invalid.

Common situations: Container stopped/restarted mid-command; Docker daemon outage or socket permission issues; race where the exec completes and is reaped before attach; Docker API version mismatches.

Related errors


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