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
- Sanitize the app name before passing it: strings.TrimSpace and filter to printable non-space runes.
- Ensure the name is 1-255 bytes and non-empty; shorten derived/concatenated names.
- 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
- Derive AppNames from a fixed allowlist of identifiers, never free-form user input.
- Trim and length-check names at config load time.
- Unit-test client construction with the exact names used in production.
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
- cannot use and advertise exit node at same time
- missing Confirm callback in Arguments
- missing Logf callback in Arguments
- nested signatures must nest a signature
- missing checkpoint state
AI-assisted analysis of tailscale/tailscale@a7769cbc33 (2026-08-27).
Data as JSON: /api/errors/2734026a164c22d3.
Report an issue: GitHub.