dagger/dagger · error
service %s does not support terminal
Error message
service %s does not support terminal
What it means
Thrown by Service.terminal when the service was started but has no Exec function attached (running.Exec == nil, core/terminal.go:383-385). A service terminal requires a running process the user can exec into; services without an executable process (e.g. those representing only a network endpoint built from a container whose command isn't tracked as an exec) cannot host an interactive terminal.
Source
Thrown at core/terminal.go:384
defer term.Close(bkgwpb.UnknownExitStatus) // always close term; it's wrapped in a once so it won't be called multiple times
env := prepTerminalEnv(output, nil)
query, err := CurrentQuery(ctx)
if err != nil {
return err
}
svcs, err := query.Services(ctx)
if err != nil {
return err
}
running, release, err := svcs.StartResultWithDependencyExitPropagationSuppressed(ctx, svc)
if err != nil {
return err
}
defer release()
if running.Exec == nil {
return fmt.Errorf("service %s does not support terminal", svcID.Path())
}
return running.Exec(ctx, args.Cmd, env, &ServiceIO{
Stdin: term.Stdin,
Stdout: term.Stdout,
Stderr: term.Stderr,
ResizeCh: term.ResizeCh,
Interactive: true,
})
}
func prepTerminal(ctx context.Context, svcID *call.ID, execErr error) (*engineutil.TerminalClient, *termenv.Output, error) {
query, err := CurrentQuery(ctx)
if err != nil {
return nil, nil, fmt.Errorf("failed to get current query: %w", err)
}
bk, err := query.Engine(ctx)
if err != nil {
return nil, nil, fmt.Errorf("failed to get engine client: %w", err)View on GitHub (pinned to 82ba2681db)
Solutions
- Attach the terminal to the underlying Container instead of the Service (container.terminal()).
- Ensure the service's container defines a real command (WithExec) so an Exec process exists.
- Use `dagger shell`/exec into the container image manually as a fallback.
Example fix
// before
go: svc, err := client.Container().WithExec(...).AsService()
// after — attach terminal to the container directly
ctr, err := client.Container().From("alpine").WithExec(["sh"])
_, err = ctr.Terminal() Defensive patterns
Strategy: validation
Validate before calling
// Prefer terminal on a Container that has a real command, not a bare Service.
const ctr = client.container().from("alpine").withExec(["sh", "-c", "sleep infinity"]);
const svc = ctr.asService();
// ensure the container defines an exec process before attaching a service terminal
if (!ctr.rootfs) throw new Error("terminal requires a container with an exec command"); Try / catch
try {
await svc.terminal();
} catch (e) {
if (String(e).includes("does not support terminal")) {
// fall back to terminal on the underlying container
await container.terminal();
} else throw e;
} Prevention
- Only terminal into services whose container ran a real command (WithExec).
- Attach terminals to Containers rather than Services when in doubt.
- Avoid creating services purely from port/proxy configuration for terminal use.
When it happens
Trigger: Attaching a terminal to a Service (Service.terminal / `dagger terminal` on a service) whose running state has Exec == nil — i.e. the service has no associated command process to attach to.
Common situations: Running `dagger call ... terminal` on a service created purely from ports/proxies, or a service whose container never ran a command, or SDK-generated services without explicit exec processes.
Related errors
- terminal not supported on non-container services
- decode persisted container withDefaultTerminalCmd lazy paylo
- failed to get caller implementation-scoped module: %w
- failed to get caller implementation-scoped module digest: %w
- get current query: %w
AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05).
Data as JSON: /api/errors/dac0b003c91f5a8f.
Report an issue: GitHub.