bytedance/deer-flow · error · ValueError

not bech32: {value!r}

Error message

not bech32: {value!r}

What it means

Part of the minimal bech32 decoder used for Nostr nsec/npub keys: _bech32_decode requires a '1' separator between the human-readable part (hrp) and the data part. If the input contains no '1' at all, it cannot even be split, so this ValueError is raised. nsec/npub keys always look like <hrp>1<data>.

Source

Thrown at backend/app/channels/buzz_nostr.py:47

class NostrKeys:
    secret: bytes
    pubkey_hex: str


def _bech32_polymod(values: list[int]) -> int:
    gen = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3]
    chk = 1
    for v in values:
        b = chk >> 25
        chk = (chk & 0x1FFFFFF) << 5 ^ v
        for i in range(5):
            chk ^= gen[i] if ((b >> i) & 1) else 0
    return chk


def _bech32_decode(expected_hrp: str, value: str) -> bytes:
    if "1" not in value:
        raise ValueError(f"not bech32: {value!r}")
    hrp, data_part = value.rsplit("1", 1)
    if hrp != expected_hrp:
        raise ValueError(f"expected {expected_hrp!r} bech32, got {hrp!r}")
    try:
        data = [_BECH32_CHARSET.index(c) for c in data_part]
    except ValueError as exc:
        raise ValueError(f"invalid bech32 character in {value!r}") from exc
    hrp_expanded = [ord(c) >> 5 for c in hrp] + [0] + [ord(c) & 31 for c in hrp]
    if _bech32_polymod(hrp_expanded + data) != 1:
        raise ValueError(f"bad bech32 checksum in {value!r}")
    acc = bits = 0
    out = bytearray()
    for v in data[:-6]:
        acc = (acc << 5) | v
        bits += 5
        if bits >= 8:
            bits -= 8
            out.append((acc >> bits) & 0xFF)

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Re-copy the full key from your Nostr wallet/client; nsec1... and npub1... always contain a '1' right after the prefix.
  2. Prefer pasting the raw 64-char hex secret instead — _parse_32_bytes also accepts plain hex.
  3. Check for truncation or invisible characters; the value is used as-is after strip().
  4. Never commit the fixed key — rotate it if it was logged.

Example fix

# before
channels:
  buzz:
    private_key: "nsec"

# after (full key or hex)
channels:
  buzz:
    private_key: "nsec1<full-data-part>"
Defensive patterns

Strategy: validation

Validate before calling

def looks_bech32(value: str, hrp: str) -> bool:
    v = value.strip().lower()
    return v.startswith(hrp) and '1' in v[len(hrp):]

Try / catch

try:
    keys = parse_private_key(raw)
except ValueError as e:
    raise SystemExit(f'bad buzz private_key: {e}') from e

Prevention

When it happens

Trigger: channels.buzz config passes a value to parse_private_key/parse_pubkey that starts with 'nsec'/'npub' (or is detected as bech32) but has no '1' separator — e.g. 'nsec', 'npubXYZ', or a truncated key pasted without its data part.

Common situations: Copy-paste of a Nostr key that got truncated; typing a key by hand; pasting a hex key with a stray 'nsec' prefix glued on; whitespace-only partial values after strip().

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/3653fa5b0e2bdb3d. Report an issue: GitHub.