ory/hydra · error

could not start debug server on port %d: %w

Error message

could not start debug server on port %d: %w

What it means

The perform device flow command in cmd/cmd_perform_device_flow.go starts a local debug HTTP server; this error is returned when net.Listen on 127.0.0.1:<port> fails. It wraps the underlying OS error (typically address already in use or permission denied).

Source

Thrown at cmd/cmd_perform_device_flow.go:46

)

func NewPerformDeviceCodeCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:     "device-code",
		Example: "{{ .CommandPath }} --client-id ...",
		Short:   "Example OAuth 2.0 Client performing the OAuth 2.0 Device Code Flow",
		Long: `Performs the device code flow. Useful for getting an access token and an ID token in machines without a browser.
The client that will be used MUST use the "none" or "client_secret_post" token-endpoint-auth-method.`,
		RunE: func(cmd *cobra.Command, args []string) error {
			client, endpoint, err := cliclient.NewClient(cmd)
			if err != nil {
				return err
			}

			if port := flagx.MustGetInt(cmd, "port"); port >= 0 {
				listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
				if err != nil {
					return fmt.Errorf("could not start debug server on port %d: %w", port, err)
				}
				srv := http.Server{Handler: newDeviceSrv(client)}
				go func() {
					if err := srv.Serve(listener); err != nil && err != http.ErrServerClosed {
						_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Debug server error: %v\n", err)
					}
				}()
				defer srv.Close() //nolint:errcheck
			}

			endpoint = cliclient.GetOAuth2URLOverride(cmd, endpoint)

			ctx := context.WithValue(cmd.Context(), oauth2.HTTPClient, client)
			scopes := flagx.MustGetStringSlice(cmd, "scope")
			deviceAuthUrl := flagx.MustGetString(cmd, "device-auth-url")
			tokenUrl := flagx.MustGetString(cmd, "token-url")
			audience := flagx.MustGetStringSlice(cmd, "audience")

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Pick a different free port, e.g. --port 4446
  2. Find and stop the process holding the port: lsof -i :<port> or ss -ltnp
  3. Use a port >= 1024 if running unprivileged
  4. Check that 127.0.0.1 loopback is available (containers/network namespaces)

Example fix

// before
hydra perform device flow --port 8080
// after
hydra perform device flow --port 8081  # 8080 was occupied
Defensive patterns

Strategy: validation

Validate before calling

port := flagx.MustGetInt(cmd, "port")
if ln, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)); err != nil {
    return fmt.Errorf("port %d unavailable: %w", port, err)
} else { ln.Close() }

Try / catch

if err != nil {
    fmt.Fprintf(os.Stderr, "port %d busy (%v): stop the other process or pick another port\n", port, err)
    os.Exit(1)
}

Prevention

When it happens

Trigger: Running `hydra perform device flow --port N` where port N is already bound by another process, or N is a privileged port (<1024) without permissions, or the loopback interface is unavailable.

Common situations: Previous debug server instance still running and holding the port, another dev service (webpack, vite) on the same port, running with --port 80 in a non-root container.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/b6afe2530f6b50e2. Report an issue: GitHub.