{"record":{"id":"a5f49704f0aa8927","repo":"bytedance/deer-flow","slug":"expected-expected-hrp-r-bech32-got-hrp-r","errorCode":null,"errorMessage":"expected {expected_hrp!r} bech32, got {hrp!r}","messagePattern":"expected (.+?) bech32, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/app/channels/buzz_nostr.py","lineNumber":50,"sourceCode":"\n\ndef _bech32_polymod(values: list[int]) -> int:\n    gen = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3]\n    chk = 1\n    for v in values:\n        b = chk >> 25\n        chk = (chk & 0x1FFFFFF) << 5 ^ v\n        for i in range(5):\n            chk ^= gen[i] if ((b >> i) & 1) else 0\n    return chk\n\n\ndef _bech32_decode(expected_hrp: str, value: str) -> bytes:\n    if \"1\" not in value:\n        raise ValueError(f\"not bech32: {value!r}\")\n    hrp, data_part = value.rsplit(\"1\", 1)\n    if hrp != expected_hrp:\n        raise ValueError(f\"expected {expected_hrp!r} bech32, got {hrp!r}\")\n    try:\n        data = [_BECH32_CHARSET.index(c) for c in data_part]\n    except ValueError as exc:\n        raise ValueError(f\"invalid bech32 character in {value!r}\") from exc\n    hrp_expanded = [ord(c) >> 5 for c in hrp] + [0] + [ord(c) & 31 for c in hrp]\n    if _bech32_polymod(hrp_expanded + data) != 1:\n        raise ValueError(f\"bad bech32 checksum in {value!r}\")\n    acc = bits = 0\n    out = bytearray()\n    for v in data[:-6]:\n        acc = (acc << 5) | v\n        bits += 5\n        if bits >= 8:\n            bits -= 8\n            out.append((acc >> bits) & 0xFF)\n    if len(out) != 32:\n        raise ValueError(f\"expected 32-byte payload in {value!r}\")\n    return bytes(out)","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/channels/buzz_nostr.py#L32-L68","documentation":"After splitting the bech32 string at the last '1', the decoder checks the human-readable part against the expected one ('nsec' for private keys, 'npub' for public keys). A mismatch raises this error — it prevents, for example, pasting an npub where an nsec is required (or vice versa) from being silently accepted.","triggerScenarios":"parse_private_key receives an npub1... value; parse_pubkey (or allowed_users entries) receives an nsec1... value; or a key from a different bech32 scheme (e.g. lnbc..., bc1..., note1...) is pasted into a buzz config field.","commonSituations":"Swapping the public/private fields when configuring buzz (private_key vs allowed_users); pasting a Lightning or Bitcoin bech32 address by accident; mixing keys from another Nostr tool with different hrp conventions.","solutions":["Use nsec1... only for channels.buzz.private_key and npub1... for allowed_users entries.","If the key came from another tool, re-export it in Nostr format (nsec/npub) or convert to 64-char hex.","Double-check you did not swap the two fields in config.yaml."],"exampleFix":"# before\nchannels:\n  buzz:\n    private_key: \"npub1abcdef...\"   # wrong hrp\n\n# after\nchannels:\n  buzz:\n    private_key: \"nsec1abcdef...\"","handlingStrategy":"validation","validationCode":"def key_matches_field(value: str, want: str) -> bool:\n    return value.strip().lower().startswith(want + '1')\n\nassert key_matches_field(cfg['private_key'], 'nsec')\nassert all(key_matches_field(u, 'npub') for u in cfg.get('allowed_users', []))","typeGuard":null,"tryCatchPattern":"try:\n    parse_private_key(v)\nexcept ValueError as e:\n    if 'bech32' in str(e):\n        hint = 'did you paste an npub where an nsec belongs (or vice versa)?'\n        raise SystemExit(f'{e} — {hint}') from e\n    raise","preventionTips":["Keep private keys (nsec) and pubkeys (npub) in clearly named config fields.","Add a config-schema lint that checks hrp prefixes per field.","Never log the rejected key value at full verbosity — it may be a real secret with one typo."],"tags":["buzz","nostr","bech32","validation"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}