dagger/dagger · error
starting client terminal session: %w
Error message
starting client terminal session: %w
What it means
Returned by TerminalProxy.Session when opening the outbound gRPC client stream to the upstream terminal service fails. The proxy accepts an incoming Session stream, then dials a client Session on the target engine; if that Dial/Session call errors (connection refused, unavailable, auth), the proxy returns this wrapped error.
Source
Thrown at engine/session/terminal/terminal.go:200
}
func NewTerminalProxy(client TerminalClient) TerminalProxy {
return TerminalProxy{
client: client,
}
}
func (p TerminalProxy) Register(srv *grpc.Server) {
RegisterTerminalServer(srv, p)
}
func (p TerminalProxy) Session(stream Terminal_SessionServer) error {
ctx, cancel := context.WithCancelCause(stream.Context())
defer cancel(errors.New("proxy stream closed"))
clientStream, err := p.client.Session(grpcutil.IncomingToOutgoingContext(ctx))
if err != nil {
return fmt.Errorf("starting client terminal session: %w", err)
}
return grpcutil.ProxyStream[anypb.Any](ctx, clientStream, stream)
}
View on GitHub (pinned to 82ba2681db)
Solutions
- Check connectivity to the target engine (dagger core dump / engine status) and retry
- Ensure the engine session is fully started before attaching a terminal
- Inspect proxy stream context cancellation — a cancelled incoming stream aborts the client dial
- Verify TLS/metadata (grpcutil.IncomingToOutgoingContext) forwards required auth headers
Example fix
// before
clientStream, err := p.client.Session(grpcutil.IncomingToOutgoingContext(ctx))
if err != nil { return fmt.Errorf("starting client terminal session: %w", err) }
// after
clientStream, err := p.client.Session(grpcutil.IncomingToOutgoingContext(ctx))
if err != nil {
if status.Code(err) == codes.Unavailable { /* retry with backoff */ }
return fmt.Errorf("starting client terminal session: %w", err)
} Defensive patterns
Strategy: retry
Validate before calling
# shell: confirm the engine is reachable before opening a terminal session
dagger query --help >/dev/null 2>&1 || { echo 'engine unreachable'; exit 1; } Try / catch
err := openTerminalSession(ctx)
if err != nil {
if status.Code(errors.Unwrap(err)) == codes.Unavailable {
// backoff and reconnect to the engine, then retry
return retryWithBackoff(openTerminalSession, ctx)
}
return err
} Prevention
- Verify engine connectivity before interactive attach
- Wait for engine/session startup completion before proxying terminals
- Forward auth/TLS metadata via grpcutil.IncomingToOutgoingContext
When it happens
Trigger: A TerminalProxy is registered (e.g. engine-to-engine or session-to-CLI proxying) and p.client.Session(ctx) fails at stream creation — target engine unreachable, gRPC server not yet serving, context already cancelled, or TLS/auth rejection.
Common situations: dagger terminal against a remote/cloud engine where the engine connection dropped; nested terminal proxying (dagger-in-dagger) where the inner engine hasn't started; network partitions or VPN drops during session attach.
Related errors
- failed to get main client caller: %w
- buildkit client: %w
- create client filesync source diffcopy stream: %w
- create client filesync target diffcopy stream: %w
- failed to open terminal: %w
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/888ff57af4b38b2c.
Report an issue: GitHub.