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

  1. Attach the terminal to the underlying Container instead of the Service (container.terminal()).
  2. Ensure the service's container defines a real command (WithExec) so an Exec process exists.
  3. 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

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


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/dac0b003c91f5a8f. Report an issue: GitHub.