HelloZeroNet/ZeroNet · error · VerifyError
Valid signs: %s/%s
Error message
Valid signs: %s/%s
What it means
verifyFile raises VerifyError('Valid signs: %s/%s') when, after checking each valid signer's signature in content['signs'], the count of valid signatures (valid_signs) is below signs_required. Multi-signature content.json must collect enough valid signatures from the authorized signer list; failing that, the content is rejected. The message shows valid count vs required count.
Source
Thrown at src/Content/ContentManager.py:998
valid_signers = self.getValidSigners(inner_path, new_content)
signs_required = self.getSignsRequired(inner_path, new_content)
if inner_path == "content.json" and len(valid_signers) > 1: # Check signers_sign on root content.json
signers_data = "%s:%s" % (signs_required, ",".join(valid_signers))
if not CryptBitcoin.verify(signers_data, self.site.address, new_content["signers_sign"]):
raise VerifyError("Invalid signers_sign!")
if inner_path != "content.json" and not self.verifyCert(inner_path, new_content): # Check if cert valid
raise VerifyError("Invalid cert!")
valid_signs = 0
for address in valid_signers:
if address in signs:
valid_signs += CryptBitcoin.verify(sign_content, address, signs[address])
if valid_signs >= signs_required:
break # Break if we has enough signs
if valid_signs < signs_required:
raise VerifyError("Valid signs: %s/%s" % (valid_signs, signs_required))
else:
return self.verifyContent(inner_path, new_content)
else: # Old style signing
raise VerifyError("Invalid old-style sign")
except Exception as err:
self.log.warning("%s: verify sign error: %s" % (inner_path, Debug.formatException(err)))
raise err
else: # Check using sha512 hash
file_info = self.getFileInfo(inner_path)
if file_info:
if CryptHash.sha512sum(file) != file_info.get("sha512", ""):
raise VerifyError("Invalid hash")
if file_info.get("size", 0) != file.tell():
raise VerifyError(
"File size does not match %s <> %s" %View on GitHub (pinned to 454c0b2e7e)
Solutions
- Have enough valid signers re-sign the exact current content: each signer runs CryptBitcoin.sign(sign_content, their_privatekey) and adds it to content['signs'][address]
- Lower signs_required in the content rules if fewer signatures are intended (requires re-sign/signers_sign update)
- Ensure no field changed after signatures were collected — sign_content is derived from the content itself, so any edit invalidates all signs
- Confirm signer addresses are in the valid_signers list; signatures from non-listed addresses don't count
Example fix
// before: only 1 of 2 required signatures
{"signs": {"1SignerA": "sigA"}} // signs_required: 2
// after: second signer adds their signature
{"signs": {"1SignerA": "sigA", "1SignerB": "sigB"}} Defensive patterns
Strategy: validation
Validate before calling
from Crypt import CryptBitcoin
content = json.load(open('content.json'))
sign_content = content_manager.signContentJson(content) # reconstruct signed body
valid = sum(1 for addr, sig in content.get('signs', {}).items()
if CryptBitcoin.verify(sign_content, addr, sig))
required = content_manager.getSignsRequired(inner_path, content)
assert valid >= required, f'only {valid}/{required} valid signatures' Try / catch
from Content.ContentManager import VerifyError
try:
site.content_manager.isModified(inner_path, file)
except VerifyError as e:
if str(e).startswith('Valid signs:'):
collect_missing_signatures() # ask co-signers to re-sign current content
else:
raise Prevention
- Freeze content edits until all required signers have signed
- Re-collect signatures after ANY content change
- Keep signs_required no higher than the number of active co-signers
When it happens
Trigger: isModified -> verifyFile counts CryptBitcoin.verify(sign_content, address, signs[address]) over valid_signers; fewer than getSignsRequired() signatures verify (missing signatures, signatures over stale sign_content after any content field change, or wrong signing keys).
Common situations: A co-author edited content.json (changing sign_content) so other signers' signatures no longer match; one signer hasn't re-signed after an update; signs_required raised without collecting the extra signature; signatures from addresses not in valid_signers are ignored.
Related errors
- Invalid signers_sign!
- Invalid old-style sign
- Includes not allowed
- Invalid json file: %s
- We have newer (Our: %s, Sent: %s)
AI-assisted analysis of HelloZeroNet/ZeroNet@454c0b2e7e (2026-09-02).
Data as JSON: /api/errors/4b9f3621c8e66786.
Report an issue: GitHub.