HelloZeroNet/ZeroNet · error · VerifyError

Invalid hash

Error message

Invalid hash

What it means

verifyPiece() hashes the downloaded piece with sha512 and compares it against the expected digest stored in the piecemap. A mismatch means the piece data is corrupt, tampered with, or does not match the published piecemap, so verification fails with VerifyError('Invalid hash').

Source

Thrown at plugins/Bigfile/BigfilePlugin.py:399

        return back

    def getPiecemap(self, inner_path):
        file_info = self.site.content_manager.getFileInfo(inner_path)
        piecemap_inner_path = helper.getDirname(file_info["content_inner_path"]) + file_info["piecemap"]
        self.site.needFile(piecemap_inner_path, priority=20)
        piecemap = Msgpack.unpack(self.site.storage.open(piecemap_inner_path, "rb").read())[helper.getFilename(inner_path)]
        piecemap["piece_size"] = file_info["piece_size"]
        return piecemap

    def verifyPiece(self, inner_path, pos, piece):
        try:
            piecemap = self.getPiecemap(inner_path)
        except Exception as err:
            raise VerifyError("Unable to download piecemap: %s" % Debug.formatException(err))

        piece_i = int(pos / piecemap["piece_size"])
        if CryptHash.sha512sum(piece, format="digest") != piecemap["sha512_pieces"][piece_i]:
            raise VerifyError("Invalid hash")
        return True

    def verifyFile(self, inner_path, file, ignore_same=True):
        if "|" not in inner_path:
            return super(ContentManagerPlugin, self).verifyFile(inner_path, file, ignore_same)

        inner_path, file_range = inner_path.split("|")
        pos_from, pos_to = map(int, file_range.split("-"))

        return self.verifyPiece(inner_path, pos_from, file)

    def optionalDownloaded(self, inner_path, hash_id, size=None, own=False):
        if "|" in inner_path:
            inner_path, file_range = inner_path.split("|")
            pos_from, pos_to = map(int, file_range.split("-"))
            file_info = self.getFileInfo(inner_path)

            # Mark piece downloaded

View on GitHub (pinned to 454c0b2e7e)

Solutions

  1. Delete the affected file from the site data directory and re-download it from other peers
  2. Verify with multiple peers — if only one serves bad data, blacklist/mute that peer and refetch
  3. If you are the site owner: repack the big file and re-sign content.json so piecemap hashes match the current file
  4. Check that pos passed to verifyPiece is piece-aligned (int(pos / piece_size) must be the intended piece index)

Example fix

// before
verifyPiece(inner_path, pos, piece)  # pos from a stale/offset cursor
// after
piece_size = piecemap['piece_size']
assert pos % piece_size == 0, 'piece position not aligned'
verifyPiece(inner_path, pos, piece)
Defensive patterns

Strategy: try-catch

Validate before calling

# detect file-modified-after-signing
local_hash = CryptHash.sha512sum(open(file_path,'rb').read(), format='digest')
expected = content_json['files'][inner_path]['sha512']
if local_hash != expected: re_pack_and_sign_site()

Try / catch

try:
    site.verifyFile(inner_path, f)
except VerifyError as err:
    if str(err) == 'Invalid hash':
        delete_corrupted_piece_and_redownload(inner_path, pos)
    else:
        raise

Prevention

When it happens

Trigger: A downloaded piece from a peer differs from the published sha512 digest: corrupted transfer, malicious peer serving wrong data, position math mismatch (pos not aligned to piece boundaries), or stale piecemap after the big file was modified but not repacked.

Common situations: Bit-flip corruption on disk/network; peer serving modified content; file edited/re-encoded after content.json/piecemap was signed; mismatched piece_size between the file and piecemap.

Related errors


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