gastownhall/beads · error
identity: invalid request nonce
Error message
identity: invalid request nonce
What it means
decodeIdentNonce hex-decodes a request nonce and requires it to be exactly identNonceBytes (16) raw bytes. SignIdentReply calls it to bind the reply MAC to the request; this error means the nonce string was not valid hex or had the wrong length, so no reply can be signed or verified.
Source
Thrown at internal/storage/dbproxy/identity/control.go:124
}
signed, err := SignIdentReply(reply, secret, nonce)
if err != nil {
return fmt.Errorf("identity: authenticate reply: %w", err)
}
want, err := hex.DecodeString(signed.MAC)
if err != nil {
return fmt.Errorf("identity: decode expected reply MAC: %w", err)
}
if !hmac.Equal(got, want) {
return errors.New("identity: reply authentication failed")
}
return nil
}
func decodeIdentNonce(nonce string) ([]byte, error) {
raw, err := hex.DecodeString(nonce)
if err != nil || len(raw) != identNonceBytes {
return nil, errors.New("identity: invalid request nonce")
}
return raw, nil
}
func canonicalIdentReply(reply IdentReply) ([]byte, error) {
payload := struct {
Schema int `json:"schema"`
Role string `json:"role"`
RootID string `json:"root_id"`
UpstreamID string `json:"upstream_id"`
PID int `json:"pid"`
Birth string `json:"birth"`
DataPort int `json:"data_port"`
ControlPort int `json:"control_port"`
}{
Schema: reply.Schema,
Role: reply.Role,
RootID: reply.RootID,View on GitHub (pinned to 71377f2769)
Solutions
- Generate nonces as crypto/rand 16 bytes and hex-encode them (32-char string) on the requester side
- Check that the nonce string was not truncated or whitespace-mangled in transit or in config
- Use the package's own nonce-generation helpers instead of hand-rolling in tests
- If verifying a captured reply, confirm the nonce matches the one sent with the original request
Example fix
// before
nonce := fmt.Sprintf("%x", time.Now().UnixNano()) // wrong size
signed, err := identity.SignIdentReply(reply, secret, nonce)
// after
raw := make([]byte, 16)
rand.Read(raw)
nonce := hex.EncodeToString(raw) // exactly 32 hex chars / 16 bytes
signed, err := identity.SignIdentReply(reply, secret, nonce) Defensive patterns
Strategy: validation
Validate before calling
func validNonce(n string) bool { raw, err := hex.DecodeString(n); return err == nil && len(raw) == 16 } Type guard
func isInvalidNonce(err error) bool { return strings.Contains(err.Error(), "invalid request nonce") } Try / catch
if err != nil {
if isInvalidNonce(err) { return ErrBugInCaller } // nonce comes from your own code
return err
} Prevention
- Generate nonces with crypto/rand: 16 bytes, hex-encoded (32 chars)
- Use the package's nonce helpers instead of hand-rolling
- Never truncate or whitespace-trim nonce strings in logs/config round-trips
- Keep nonce size consistent across protocol versions
When it happens
Trigger: Calling SignIdentReply with a nonce that is empty, truncated, non-hex, or generated at the wrong size (not 16 random bytes hex-encoded to 32 chars) — e.g. hand-rolled nonce generation in tests or tooling.
Common situations: Custom test harnesses producing non-standard nonces; truncation when logging/serializing the nonce; version mismatch where one side uses a different nonce length.
Related errors
- identity: invalid proxy secret
- no store is open for this workspace
- not found
- no absolute native user directory is available
- ExternalDoltConfig: set either Socket OR (Host, Port), not b
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a44af39d091b50ba.
Report an issue: GitHub.