tailscale/tailscale · error

invalid AppName %.40q

Error message

invalid AppName %.40q

What it means

derp.NewClient validates the configured AppName via derp.ValidAppName and rejects anything invalid. AppName must be a non-empty, trimmed string of at most 255 bytes containing only printable, non-space runes ( effectively letters, digits, and punctuation like '-', '_', '.'). This guards the DERPI field on the wire so servers don't receive garbage or overlong identifiers.

Source

Thrown at derp/derp_client.go:123

	}
	for i := range len(name) {
		if b := name[i]; b < ' ' || b > '~' {
			return false
		}
	}
	return true
}

func NewClient(privateKey key.NodePrivate, nc Conn, brw *bufio.ReadWriter, logf logger.Logf, opts ...ClientOpt) (*Client, error) {
	var opt clientOpt
	for _, o := range opts {
		if o == nil {
			return nil, errors.New("nil ClientOpt")
		}
		o.update(&opt)
	}
	if !ValidAppName(opt.AppName) {
		return nil, fmt.Errorf("invalid AppName %.40q", opt.AppName)
	}
	return newClient(privateKey, nc, brw, logf, opt)
}

func newClient(privateKey key.NodePrivate, nc Conn, brw *bufio.ReadWriter, logf logger.Logf, opt clientOpt) (*Client, error) {
	c := &Client{
		privateKey:  privateKey,
		publicKey:   privateKey.Public(),
		logf:        logf,
		nc:          nc,
		br:          brw.Reader,
		bw:          brw.Writer,
		meshKey:     opt.MeshKey,
		canAckPings: opt.CanAckPings,
		isProber:    opt.IsProber,
		appName:     opt.AppName,
		clock:       tstime.StdClock{},
	}

View on GitHub (pinned to a7769cbc33)

Solutions

  1. Sanitize the app name before passing it: strings.TrimSpace and filter to printable non-space runes.
  2. Ensure the name is 1-255 bytes and non-empty; shorten derived/concatenated names.
  3. If the value comes from config/env, validate it at startup with derp.ValidAppName and fail fast with a clear message.

Example fix

// before
c, err := derp.NewClient(key, conn, brw, logf, derp.AppName(rawName))

// after
name := strings.TrimSpace(rawName)
if !derp.ValidAppName(name) {
    log.Fatalf("bad app name %q", name)
}
c, err := derp.NewClient(key, conn, brw, logf, derp.AppName(name))
Defensive patterns

Strategy: validation

Validate before calling

name := strings.TrimSpace(raw)
if len(name) == 0 || len(name) > 255 || strings.ContainsFunc(name, func(r rune) bool { return r <= ' ' || r > '~' }) {
    return fmt.Errorf("invalid app name %q", raw)
}
// or simply:
if !derp.ValidAppName(raw) { return fmt.Errorf("invalid app name %q", raw) }

Type guard

func ValidAppName(s string) bool {
	if len(s) == 0 || len(s) > 255 { return false }
	for _, r := range s {
		if r <= ' ' || r > '~' { return false }
	}
	return true
}

Try / catch

c, err := derp.NewClient(k, nc, brw, logf, derp.AppName(name))
if err != nil {
    if strings.Contains(err.Error(), "invalid AppName") {
    	return fmt.Errorf("app name %q rejected by DERP: %w", name, err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling derp.NewClient (or NewNetworkClient) with a ClientOpt (e.g. derp.MeshKey, derp.CancelForwardingConfig... specifically derp.AppName("...")) whose value contains whitespace/control chars, leading/trailing spaces, non-printable Unicode, or exceeds 255 bytes; also passing an empty AppName.

Common situations: Copying an AppName from config with trailing newline or space; using a UUID with spaces; embedding a user-controlled string into the app name; upgrading to a tailscale version where AppName validation was added (older versions accepted anything).

Related errors


AI-assisted analysis of tailscale/tailscale@a7769cbc33 (2026-08-27). Data as JSON: /api/errors/2734026a164c22d3. Report an issue: GitHub.