juanfont/headscale · error

converting private key to string for saving: %w

Error message

converting private key to string for saving: %w

What it means

Returned by readOrCreatePrivateKey when key.NewMachine().MarshalText() fails while persisting a newly generated noise private key (hscontrol/app.go:969). MachinePrivate.MarshalText encodes the key to its textual form; for a valid in-memory key this operation is effectively infallible in tailscale.com/util/key — this error is a defensive guard that essentially never fires in practice.

Source

Thrown at hscontrol/app.go:971

}

func readOrCreatePrivateKey(path string) (*key.MachinePrivate, error) {
	dir := filepath.Dir(path)

	err := util.EnsureDir(dir)
	if err != nil {
		return nil, fmt.Errorf("ensuring private key directory: %w", err)
	}

	privateKey, err := os.ReadFile(path)
	if errors.Is(err, os.ErrNotExist) {
		log.Info().Str("path", path).Msg("no private key file at path, creating...")

		machineKey := key.NewMachine()

		machineKeyStr, err := machineKey.MarshalText()
		if err != nil {
			return nil, fmt.Errorf(
				"converting private key to string for saving: %w",
				err,
			)
		}

		err = os.WriteFile(path, machineKeyStr, privateKeyFileMode)
		if err != nil {
			return nil, fmt.Errorf(
				"saving private key to disk at path %q: %w",
				path,
				err,
			)
		}

		return &machineKey, nil
	} else if err != nil {
		return nil, fmt.Errorf("reading private key file: %w", err)
	}

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Rebuild/reinstall headscale from a clean checkout so the vendored tailscale.com/util/key version matches (go mod tidy && make build).
  2. If maintaining a fork, verify the vendored key package is unmodified.
  3. Treat as a build-environment problem: clear the module cache (go clean -modcache) and rebuild.
Defensive patterns

Strategy: try-catch

Try / catch

// Practically unreachable; treat any occurrence as a build/library mismatch.
if err := h.Serve(); err != nil && strings.Contains(err.Error(), "converting private key to string") {
    log.Fatal().Msg("binary built against incompatible tailscale key package; rebuild cleanly")
}

Prevention

When it happens

Trigger: Only conceivable if the key type's MarshalText signature gains a failure mode in a future tailscale.com/util/key version (e.g. invalid internal state). No current input, config, or environment triggers it.

Common situations: Virtually none; if it appears, it indicates a corrupted build or an incompatible tailscale library version linked into a custom compile of headscale.

Related errors


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