juanfont/headscale · error

creating user: %w

Error message

creating user: %w

What it means

"creating user: %w" at cmd/dev/main.go:166 wraps runHS(ctx, hsBin, configPath, "users", "create", "dev", "-o", "json") in cmd/dev. After the server is healthy, the tool creates a user named 'dev' via the headscale CLI embedded in the just-built binary; failure is either the CLI exiting non-zero (invalid name, DB error) or the command failing to run. CLI stderr/stdout surface the underlying cause.

Source

Thrown at cmd/dev/main.go:166

	err = serve.Start()
	if err != nil {
		return fmt.Errorf("starting headscale: %w", err)
	}

	// Wait for server to be ready.
	healthURL := fmt.Sprintf("http://127.0.0.1:%d/health", *port)

	err = waitForHealth(ctx, healthURL, 30*time.Second)
	if err != nil {
		return fmt.Errorf("waiting for headscale: %w", err)
	}

	// Create user.
	fmt.Println("Creating user and pre-auth key...")

	userJSON, err := runHS(ctx, hsBin, configPath, "users", "create", "dev", "-o", "json")
	if err != nil {
		return fmt.Errorf("creating user: %w", err)
	}

	userID, err := extractUserID(userJSON)
	if err != nil {
		return fmt.Errorf("parsing user: %w", err)
	}

	// Create pre-auth key.
	keyJSON, err := runHS(
		ctx, hsBin, configPath,
		"preauthkeys", "create",
		"-u", strconv.FormatUint(userID, 10),
		"--reusable",
		"-e", "24h",
		"-o", "json",
	)
	if err != nil {
		return fmt.Errorf("creating pre-auth key: %w", err)

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Delete the existing user first: `<hsBin> -c <config> users delete dev`, or point the dev state dir at a fresh location
  2. Check the CLI output above the error for the exact refusal (duplicate user, DB lock) and address it
  3. Run without -keep so the whole scratch state (including the DB) is recreated each time

Example fix

# before
# stale state: user 'dev' already exists from a prior -keep run

# after
headscale -c /tmp/headscale-dev-*/config.yaml users delete dev
# or simply drop the old state dir and re-run: go run ./cmd/dev
Defensive patterns

Strategy: try-catch

Try / catch

userJSON, err := runHS(ctx, hsBin, configPath, "users", "create", "dev", "-o", "json")
if err != nil {
	if strings.Contains(err.Error(), "already exists") {
		// reuse or delete the existing 'dev' user, then continue
	}
	return fmt.Errorf("creating user: %w", err)
}

Prevention

When it happens

Trigger: A user 'dev' already exists (CLI reports duplicate); the server's SQLite database path is unwritable; the CLI subprocess was killed by ctx cancellation; the built binary misbehaves relative to the current config schema.

Common situations: Re-running cmd/dev against a persistent state directory where 'dev' was already created; leftover state from a previous run if -keep was used.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/29d4155eeb60a35f. Report an issue: GitHub.