MHSanaei/3x-ui · error
wireguard publicKey: %w
Error message
wireguard publicKey: %w
What it means
Wireguard branch of the Xray account builder: the required 'publicKey' passed the presence check but wgutil.KeyToHex rejected it. KeyToHex base64-decodes the value and expects exactly the 32-byte Wireguard key length, so this wraps either a base64 decode error or a wrong-length error — the publicKey is not a valid Wireguard public key.
Source
Thrown at internal/xray/api.go:630
CipherType: ssCipherType,
}), nil
case "hysteria":
auth, err := getRequiredUserString(user, "auth")
if err != nil {
return nil, err
}
return serial.ToTypedMessage(&hysteriaAccount.Account{
Auth: auth,
}), nil
case "wireguard":
pubB64, err := getRequiredUserString(user, "publicKey")
if err != nil {
return nil, err
}
pubHex, err := wgutil.KeyToHex(pubB64)
if err != nil {
return nil, fmt.Errorf("wireguard publicKey: %w", err)
}
pskB64, err := getOptionalUserString(user, "preSharedKey")
if err != nil {
return nil, err
}
pskHex, err := wgutil.KeyToHex(pskB64)
if err != nil {
return nil, fmt.Errorf("wireguard preSharedKey: %w", err)
}
allowed := collectStringSlice(user["allowedIPs"])
if len(allowed) == 0 {
return nil, common.NewError("wireguard: allowedIPs required")
}
keepAlive, err := getOptionalUserString(user, "keepAlive")
if err != nil {View on GitHub (pinned to ad32144c42)
Solutions
- Regenerate the keypair and paste the base64 (base64nopad) form of the PUBLIC key — 'wg pubkey' output or 'wg genkey | wg pubkey' is the canonical source; Xray expects the same 32-byte base64 form as wireguard-go.
- Verify length: base64 of 32 bytes is 43 chars without padding (44 with '='). A 64-char hex string means you copied hex — convert it (echo <hex> | xxd -r -p | base64) or re-derive from the tool.
- Ensure no leading/trailing whitespace or quotes snuck into the stored value.
Example fix
// before — hex form pasted user["publicKey"] = "8f4a1c..." // 64 hex chars // after — base64 (43-44 chars) form user["publicKey"] = "j0ocQKx+Zr1f3..." // base64 of the same 32 bytes
Defensive patterns
Strategy: validation
Validate before calling
// Validate a Wireguard key (base64, 32 bytes) before it reaches Xray
func isValidWGKeyB64(s string) bool {
s = strings.TrimSpace(s)
if len(s) == 44 && strings.HasSuffix(s, "=") {
s = s[:43]
}
if len(s) != 43 {
return false
}
raw, err := base64.RawStdEncoding.DecodeString(s)
return err == nil && len(raw) == 32
} Type guard
null
Try / catch
if err != nil && strings.Contains(err.Error(), "wireguard publicKey") {
// regenerate via `wg genkey | wg pubkey`, paste base64 form; no retry
} Prevention
- Generate keys with wg genkey/wg pubkey and copy the base64 output verbatim.
- Sanity-check length (43-44 base64 chars) at paste time in the UI.
When it happens
Trigger: Providing a wireguard client publicKey that is: hex instead of base64 (Wireguard tools often print hex), a base64 string of the wrong length, a private key pasted by mistake, or a key with whitespace/typo corruption.
Common situations: Copy-paste from 'wg show' output (hex form) into a field expecting base64; mixing up private/public keys when creating a peer; truncated keys from terminal copy; keys generated by a non-standard tool.
Related errors
- wireguard preSharedKey: %w
- invalid metric (unknown metric)
- invalid tag (unknown observatory tag)
- missing required user field %q
- The new username and password are empty
AI-assisted analysis of MHSanaei/3x-ui@ad32144c42 (2026-08-15).
Data as JSON: /api/errors/3b68f7418befcf15.
Report an issue: GitHub.