HelloZeroNet/ZeroNet · error · AnnounceError

Invalid response: %r (%s)

Error message

Invalid response: %r (%s)

What it means

The HTTP tracker replied, the body was bencode-decoded and b"peers" extracted, but parsing the 6-byte-per-peer compact format (unpack '!LH' / inet_ntoa) threw an exception. The plugin wraps it in AnnounceError("Invalid response: %r (%s)") including the raw response and the formatted exception.

Source

Thrown at plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py:146

            req.close()
            req = None

        if not response:
            raise AnnounceError("No response after %.0fs" % (time.time() - s))

        # Decode peers
        try:
            peer_data = bencode_open.loads(response)[b"peers"]
            response = None
            peer_count = int(len(peer_data) / 6)
            peers = []
            for peer_offset in range(peer_count):
                off = 6 * peer_offset
                peer = peer_data[off:off + 6]
                addr, port = struct.unpack('!LH', peer)
                peers.append({"addr": socket.inet_ntoa(struct.pack('!L', addr)), "port": port})
        except Exception as err:
            raise AnnounceError("Invalid response: %r (%s)" % (response, Debug.formatException(err)))

        return peers

View on GitHub (pinned to 454c0b2e7e)

Solutions

  1. Read the exception detail and raw response in the message — a 'failure reason' payload means the tracker rejected the announce.
  2. Ensure the announce URL requests compact responses (peer batching) as this parser only handles 6-byte compact peers.
  3. Check whether tracker returned dict-style peers and add handling for that format.
  4. Switch to a working tracker in the site's tracker list.
  5. Verify no proxy is rewriting the response body.

Example fix

// before
peer_data = bencode_open.loads(response)[b"peers"]
// after
parsed = bencode_open.loads(response)
if not isinstance(parsed.get(b"peers"), (bytes, bytearray)):
    raise AnnounceError("Tracker failure: %r" % parsed)
peer_data = parsed[b"peers"]
Defensive patterns

Strategy: validation

Validate before calling

parsed = bencode_open.loads(response)
peer_data = parsed.get(b"peers")
if isinstance(peer_data, dict):
    raise ValueError("Tracker returned non-compact peers dict")
if not isinstance(peer_data, (bytes, bytearray)) or len(peer_data) % 6 != 0:
    raise ValueError("Peers payload not 6-byte aligned compact format")

Type guard

def is_compact_peer_list(peer_data):
    return isinstance(peer_data, (bytes, bytearray)) and len(peer_data) % 6 == 0

Try / catch

try:
    peers = announce_plugin.announceTrackerHttp(tracker_address, file_info)
except AnnounceError as e:
    log.warning("Unparseable tracker response: %s" % e)
    peers = []

Prevention

When it happens

Trigger: peer_data length is not a multiple of 6 (truncated body, HTML error page that happened to bencode-decode, dict-style peers response instead of compact string), or peer_data is a dict/list not bytes so len()/slicing misbehaves.

Common situations: Trackers returning non-compact peer lists (dict with peer id fields) when compact=1 isn't honored; trackers returning error bencode like {b'failure reason': ...} with no b'peers'; proxy-injected HTML corrupting the body.

Related errors


AI-assisted analysis of HelloZeroNet/ZeroNet@454c0b2e7e (2026-09-02). Data as JSON: /api/errors/6f925f24086715ce. Report an issue: GitHub.