HelloZeroNet/ZeroNet · error · VerifyError

Invalid cert!

Error message

Invalid cert!

What it means

verifyFile raises VerifyError('Invalid cert!') when a non-root inner_path's content.json fails self.verifyCert(inner_path, new_content). For user content (e.g. data users publish under users/<address>/content.json), the content must carry a valid certificate (cert_user_id, cert_sign, etc.) issued by a recognized cert provider and whose issuer matches a valid signer of the site. Failure means the certificate signature, issuer, or cert_user_id is missing/invalid or the issuer is not an authorized site signer.

Source

Thrown at src/Content/ContentManager.py:989

                modified = new_content["modified"]
                if config.fix_float_decimals and type(modified) is float and not str(modified).endswith(".0"):
                    modified_fixed = "{:.6f}".format(modified).strip("0.")
                    sign_content = sign_content.replace(
                        '"modified": %s' % repr(modified),
                        '"modified": %s' % modified_fixed
                    )

                if signs:  # New style signing
                    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

View on GitHub (pinned to 454c0b2e7e)

Solutions

  1. Re-obtain a valid certificate from the identity provider (e.g. via ZeroID/ZeroMe) and include the fresh cert fields (cert_user_id, cert_sign) in your content.json before signing
  2. Verify cert_user_id format is 'username@domain' and the issuer domain is accepted by the site's rules
  3. Make sure you sign your content.json with the key matching the certified address
  4. If you own the site, check that the cert issuer's address is listed among the site's valid signers

Example fix

// before: content.json missing/edited cert
{"cert_user_id": "bob@zeroid.bit", "files": {...}}  // no cert_sign or wrong cert
// after: valid cert attached then signed
{"cert_user_id": "bob@zeroid.bit", "cert_sign": "<valid_sig_from_issuer>", "files": {...}}
Defensive patterns

Strategy: validation

Validate before calling

content = json.load(open('users/<addr>/content.json'))
required = ('cert_user_id', 'cert_sign')
if not all(k in content for k in required):
    raise ValueError('missing cert fields')
user, issuer_domain = content['cert_user_id'].split('@')
# confirm issuer is an accepted cert provider of the site
valid_issuers = content_manager.getValidSigners('content.json', root_content)
assert content['cert_user_id'] in known_valid_certs, 'obtain fresh cert from provider'

Type guard

def has_cert(content):
    return bool(content.get('cert_user_id')) and bool(content.get('cert_sign'))

Try / catch

from Content.ContentManager import VerifyError
try:
    site.content_manager.isModified(inner_path, file)
except VerifyError as e:
    if str(e) == 'Invalid cert!':
        obtain_new_cert()  # re-auth with identity provider, re-sign content
    else:
        raise

Prevention

When it happens

Trigger: verifyFile with inner_path != 'content.json' calls verifyCert; it returns False because cert_sign does not verify against the cert issuer's address, cert_user_id is malformed, the issuer address is not in the site's valid signers, or the cert fields were edited after signing.

Common situations: User trying to post to a ZeroMe/Forum-style site without a valid identity cert; a revoked/expired cert from the identity provider; hand-copied content.json with a cert for a different username/address; site changed its accepted cert issuers after the user signed.

Related errors


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