{"record":{"id":"80f73dd3338deb38","repo":"OpenNHP/opennhp","slug":"relay-server-d-publickeybase64-invalid-w","errorCode":null,"errorMessage":"relay: server #%d publicKeyBase64 invalid: %w","messagePattern":"relay: server #(.+?) publicKeyBase64 invalid: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/relay/config.go","lineNumber":233,"sourceCode":"\t// change the instance\" mistake. The dedupe key is (fingerprint, addr),\n\t// NOT addr alone: resolveTarget routes by PeerPk, so two DISTINCT\n\t// pubkeys legitimately sharing one host:port (a SNI/header-routed\n\t// front-end, or port-multiplexed identities) is a valid topology and\n\t// must not be a hard config-load failure. Only same-pubkey + same-addr\n\t// is the unambiguous copy-paste error.\n\ttype addrOrigin struct {\n\t\tserver   int\n\t\tinstance int\n\t}\n\tseenAddr := make(map[string]addrOrigin)\n\tfor i := range cfg.Servers {\n\t\tc := &cfg.Servers[i]\n\t\tif c.PubKeyBase64 == \"\" {\n\t\t\treturn fmt.Errorf(\"relay: server #%d missing publicKeyBase64\", i)\n\t\t}\n\t\tfp, err := utils.PubKeyFingerprintFromBase64(c.PubKeyBase64)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"relay: server #%d publicKeyBase64 invalid: %w\", i, err)\n\t\t}\n\t\tif dup, ok := seenFP[fp]; ok {\n\t\t\treturn fmt.Errorf(\"relay: server #%d and #%d share the same publicKeyBase64 (fingerprint %s)\", dup, i, fp)\n\t\t}\n\t\tseenFP[fp] = i\n\n\t\tif len(c.Instances) == 0 {\n\t\t\treturn fmt.Errorf(\"relay: server #%d (fingerprint %s) has no [[Servers.Instances]]\", i, fp)\n\t\t}\n\t\tfor j := range c.Instances {\n\t\t\tinst := &c.Instances[j]\n\t\t\tif inst.Host == \"\" {\n\t\t\t\treturn fmt.Errorf(\"relay: server #%d instance #%d missing host\", i, j)\n\t\t\t}\n\t\t\tif inst.Port <= 0 {\n\t\t\t\treturn fmt.Errorf(\"relay: server #%d instance #%d missing or invalid port\", i, j)\n\t\t\t}\n\t\t\taddr := fmt.Sprintf(\"%s:%d\", inst.Host, inst.Port)","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/relay/config.go#L215-L251","documentation":"normalize decodes each server's publicKeyBase64 via utils.PubKeyFingerprintFromBase64 to compute a fingerprint; this error wraps the decode failure with the server index. The value must be valid base64 encoding of an acceptable public key format (e.g. Curve25519).","triggerScenarios":"PubKeyFingerprintFromBase64 returns an error for server i — the publicKeyBase64 string is not valid base64, has whitespace/quotes/newlines embedded, decodes to the wrong key length, or contains a placeholder like \"<server pubkey>\".","commonSituations":"Pasting a key with surrounding quotes or line breaks; truncating the key during copy/paste; putting an SM2 key where Curve is expected (or vice versa); placeholder text left in from a template; using the private key instead of the public key.","solutions":["Re-copy the server's public key exactly as output by keygen, without quotes, whitespace, or truncation","Verify the key decodes with `base64 -d` to the expected byte length","Confirm key algorithm matches the relay's cipher scheme (curve vs sm2) and use the corresponding keygen output","Ensure you are using the server's public key, not its private key"],"exampleFix":"// before\npubKeyBase64 = \"<server pubkey>\"\n// after\npubKeyBase64 = \"dGVzdC1wdWJsaWMta2V5LWJhc2U2NC1zdHJpbmc=\"","handlingStrategy":"validation","validationCode":"raw := strings.TrimSpace(cfg.Servers[i].PubKeyBase64)\nb, err := base64.StdEncoding.DecodeString(raw)\nif err != nil || len(b) != 32 { // curve25519\n\treturn fmt.Errorf(\"server #%d pubkey not valid base64 32-byte key\", i)\n}","typeGuard":null,"tryCatchPattern":"if err := cfg.Normalize(); err != nil {\n\tif strings.Contains(err.Error(), \"publicKeyBase64 invalid\") {\n\t\treturn fmt.Errorf(\"re-copy the server key from keygen output verbatim: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Copy keys with a tool (pbpaste/xclip) rather than retyping","Verify keys decode with `base64 -d | wc -c` before committing configs","Use the matching keygen algorithm (curve vs sm2) for your cipher scheme"],"tags":["relay","config","public-key","base64"],"backgroundTag":"invalid-argument-format","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}