tailscale/tailscale · error

invalid device name %q

Error message

invalid device name %q

What it means

NewDeeplink field check: DeviceName must be non-empty; it becomes the 'dn' query parameter of the sign deeplink so the approver can see which device they are signing. A zero-value struct field triggers this rejection before signing.

Source

Thrown at tka/deeplink.go:57

type NewDeeplinkParams struct {
	NodeKey    string
	TLPub      string
	DeviceName string
	OSName     string
	LoginName  string
}

// NewDeeplink creates a signed deeplink using the authority's stateID as a
// secret. This deeplink can then be validated by ValidateDeeplink.
func (a *Authority) NewDeeplink(params NewDeeplinkParams) (string, error) {
	if params.NodeKey == "" || !strings.HasPrefix(params.NodeKey, "nodekey:") {
		return "", fmt.Errorf("invalid node key %q", params.NodeKey)
	}
	if params.TLPub == "" || !strings.HasPrefix(params.TLPub, "tlpub:") {
		return "", fmt.Errorf("invalid tlpub %q", params.TLPub)
	}
	if params.DeviceName == "" {
		return "", fmt.Errorf("invalid device name %q", params.DeviceName)
	}
	if params.OSName == "" {
		return "", fmt.Errorf("invalid os name %q", params.OSName)
	}
	if params.LoginName == "" {
		return "", fmt.Errorf("invalid login name %q", params.LoginName)
	}

	u := url.URL{
		Scheme: DeeplinkTailscaleURLScheme,
		Host:   DeeplinkCommandSign,
		Path:   "/v1/",
	}
	v := url.Values{}
	v.Set("nk", params.NodeKey)
	v.Set("tp", params.TLPub)
	v.Set("dn", params.DeviceName)
	v.Set("os", params.OSName)

View on GitHub (pinned to 6e0912f979)

Solutions

  1. Populate DeviceName from the node's hostname/profile data before calling
  2. Default it to a generated name if the hostname is empty
  3. Validate all five params in one helper before calling NewDeeplink

Example fix

// before
p := tka.NewDeeplinkParams{NodeKey: nk, TLPub: tp, OSName: runtime.GOOS}

// after
p := tka.NewDeeplinkParams{NodeKey: nk, TLPub: tp, DeviceName: host.Name(), OSName: runtime.GOOS}
Defensive patterns

Strategy: validation

Validate before calling

if strings.TrimSpace(p.DeviceName) == "" {
    return errors.New("device name required before minting a sign deeplink")
}

Type guard

func nonEmpty(s string) bool { return strings.TrimSpace(s) != "" }

Prevention

When it happens

Trigger: NewDeeplinkParams built with DeviceName left "" - a forgotten profile field, an empty hostname, or a struct literal missing the field.

Common situations: Zero-value params structs; provisioning flows where the device hostname is not yet set when the deeplink is minted.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of tailscale/tailscale@6e0912f979 (2026-08-18). Data as JSON: /api/errors/26911992439cd87b. Report an issue: GitHub.