hashicorp/nomad · error

failed to attach to exec: %v

Error message

failed to attach to exec: %v

What it means

After creating the exec instance, the driver hijacks the HTTP connection with `ExecAttach` to stream stdin/stdout/stderr. If attach fails, the exec object exists but no I/O stream could be established, and the driver wraps the error. Cleanup of the hijacked connection is deferred only on success.

Source

Thrown at drivers/docker/driver.go:1986

			case <-ctx.Done():
				return
			case <-done:
				return
			case s, ok := <-opts.ResizeCh:
				if !ok {
					return
				}
				_, _ = client.ExecResize(d.ctx, exec.ID, mclient.ExecResizeOptions{
					Height: uint(s.Height),
					Width:  uint(s.Width),
				})
			}
		}
	}()

	resp, err := client.ExecAttach(ctx, exec.ID, mclient.ExecAttachOptions{TTY: opts.Tty})
	if err != nil {
		return nil, fmt.Errorf("failed to attach to exec: %v", err)
	}
	defer func() {
		opts.Stdin.Close() // close stdin
		resp.CloseWrite()  // close hijacked write connection
		resp.Close()       // close read connection
	}()

	go func() {
		if !opts.Tty {
			_, _ = stdcopy.StdCopy(opts.Stdout, opts.Stderr, resp.Reader)
		} else {
			_, _ = io.Copy(opts.Stdout, resp.Reader)
		}
	}()

	go func() {
		_, _ = io.Copy(resp.Conn, opts.Stdin)
		_ = resp.CloseWrite()

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Increase the exec timeout passed to the driver so attach is not raced by ctx.Done().
  2. Verify direct connectivity to dockerd (no proxy stripping HTTP upgrade headers).
  3. Retry the whole exec operation; the exec ID from a failed attach is not reusable.
  4. Check dockerd logs for exec attach failures around the timestamp.

Example fix

// before
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
Defensive patterns

Strategy: try-catch

Try / catch

stream, err := driver.ExecTaskStreaming(ctx, taskID, opts)
if err != nil {
    if strings.Contains(err.Error(), "failed to attach to exec") {
        // retry with fresh exec and a larger timeout
        ctx, cancel = context.WithTimeout(context.Background(), 60*time.Second)
    }
    return err
}

Prevention

When it happens

Trigger: client.ExecAttach(ctx, exec.ID, mclient.ExecAttachOptions{...}) errors — network interruption to dockerd, exec ID invalid/expired, context deadline exceeded while upgrading the connection.

Common situations: Timeout expired before attach (large ctx from `timeout`); dockerd restarted between create and attach; proxy/load balancer between client and daemon blocking HTTP upgrade; exec cancelled by another caller.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/876cecb1d064bf28. Report an issue: GitHub.