tailscale/tailscale · error

key[%d]: %w

Error message

key[%d]: %w

What it means

Duplicate-key detection in State.staticValidateCheckpoint must compute each key's KeyID (Key.ID()). Only Key25519 keys have a defined ID — Key.ID() returns 'unknown key kind: N' for anything else (KeyInvalid = 0, or a kind introduced by a newer version). This site wraps that error with %w, preserving errors.Unwrap, and prefixes the index i of the offending key from the outer loop. A checkpoint containing any non-25519 key can never validate.

Source

Thrown at tka/state.go:303

	if numKeys := len(s.Keys); numKeys > maxKeys {
		return fmt.Errorf("too many keys (%d, max %d)", numKeys, maxKeys)
	}
	for i, k := range s.Keys {
		if err := k.StaticValidate(); err != nil {
			return fmt.Errorf("key[%d]: %v", i, err)
		}
	}
	// NOTE: The max number of keys is constrained (512), so
	// O(n^2) is fine.
	for i, k := range s.Keys {
		for j, k2 := range s.Keys {
			if i == j {
				continue
			}

			id1, err := k.ID()
			if err != nil {
				return fmt.Errorf("key[%d]: %w", i, err)
			}
			id2, err := k2.ID()
			if err != nil {
				return fmt.Errorf("key[%d]: %w", j, err)
			}

			if bytes.Equal(id1, id2) {
				return fmt.Errorf("key[%d]: duplicates key[%d]", i, j)
			}
		}
	}
	return nil
}

// CreateStateForTest creates a [State] that marks the given keys as trusted
// with an arbitrary disablement value.
//
// This is only for use in tests, and will panic if called outside a test.

View on GitHub (pinned to 6e0912f979)

Solutions

  1. Unwrap the error — 'unknown key kind: N' tells you the numeric kind; check State.Keys[i].Kind for the index in the message.
  2. Set Kind: tka.Key25519 and Public to the 32-byte ed25519 public key on every key you trust.
  3. On version skew, upgrade tailscaled on the rejecting node so both ends understand the same key kinds.
  4. Pre-check each key with k.ID() before committing — it fails fast with the same underlying cause.

Example fix

// before
keys = append(keys, tka.Key{Public: pub, Votes: 1}) // Kind defaults to KeyInvalid -> "key[0]: unknown key kind: invalid"

// after
keys = append(keys, tka.Key{Kind: tka.Key25519, Public: pub, Votes: 1})
Defensive patterns

Strategy: validation

Validate before calling

for i, k := range keys {
	if _, err := k.ID(); err != nil {
		return fmt.Errorf("key[%d] has unsupported kind %v: %w", i, k.Kind, err)
	}
}

Prevention

When it happens

Trigger: A checkpoint AUM validated via AUM.StaticValidate (Authority.Inform/Apply, builder, verification) whose State.Keys[i].Kind is not Key25519 — typically Kind left at the zero value KeyInvalid because the struct was built without setting it.

Common situations: Constructing tka.Key without the Kind field (zero value is KeyInvalid); CBOR state decoded with the kind field absent; forward-compat rejection when a node sees a key kind from a newer Tailscale release.

Related errors


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