certbot/certbot · error · ValueError

The supplied code: %s is not a known ACME error code

Error message

The supplied code: %s is not a known ACME error code

What it means

Raised by acme.messages.Error.with_code when the given code string is not a key of the ERROR_CODES mapping of known ACME error codes. The method exists to construct typed ACME errors (e.g. 'dnssec', 'connection') by prefixing the code with the ACME urn prefix, so it refuses unknown codes.

Source

Thrown at acme/src/acme/messages.py:156

        'identifier', decoder=Identifier.from_json, omitempty=True)
    subproblems: Optional[tuple['Error', ...]] = jose.field('subproblems', omitempty=True)

    # Mypy does not understand the josepy magic happening here, and falsely claims
    # that subproblems is redefined. Let's ignore the type check here.
    @subproblems.decoder  # type: ignore
    def subproblems(value: list[dict[str, Any]]) -> tuple['Error', ...]:  # pylint: disable=no-self-argument,missing-function-docstring
        return tuple(Error.from_json(subproblem) for subproblem in value)

    @classmethod
    def with_code(cls, code: str, **kwargs: Any) -> 'Error':
        """Create an Error instance with an ACME Error code.

        :str code: An ACME error code, like 'dnssec'.
        :kwargs: kwargs to pass to Error.

        """
        if code not in ERROR_CODES:
            raise ValueError("The supplied code: %s is not a known ACME error"
                             " code" % code)
        typ = ERROR_PREFIX + code
        # Mypy will not understand that the Error constructor accepts a named argument
        # "typ" because of josepy magic. Let's ignore the type check here.
        return cls(typ=typ, **kwargs)

    @property
    def description(self) -> Optional[str]:
        """Hardcoded error description based on its type.

        :returns: Description if standard ACME error or ``None``.
        :rtype: str

        """
        return ERROR_TYPE_DESCRIPTIONS.get(self.typ)

    @property
    def code(self) -> Optional[str]:

View on GitHub (pinned to 2b817be146)

Solutions

  1. Check the code against acme.messages.ERROR_CODES and use a registered code such as 'dnssec', 'malformed', 'serverInternal'
  2. If the code comes from a real server response, parse the error from the response JSON directly (Error.from_json/typ field) instead of with_code
  3. Upgrade the acme package so newer error codes are included in ERROR_CODES

Example fix

# before
Error.with_code('dnsSEC')  # ValueError

# after
from acme import messages
assert 'dnssec' in messages.ERROR_CODES
messages.Error.with_code('dnssec')
Defensive patterns

Strategy: validation

Validate before calling

from acme import messages
if code not in messages.ERROR_CODES:
    raise KeyError(f'unknown ACME error code {code}; available: {sorted(messages.ERROR_CODES)}')

Type guard

from acme import messages
def is_known_acme_code(code: str) -> bool:
    return code in messages.ERROR_CODES

Try / catch

try:
    err = messages.Error.with_code(code)
except ValueError:
    err = messages.Error(typ='urn:ietf:params:acme:error:' + code, detail='...')

Prevention

When it happens

Trigger: Calling Error.with_code('someUnknownCode', ...) where the code is not registered in acme.messages.ERROR_CODES — e.g. typo like 'dnsSEC', a new RFC 8555 error type the installed acme version predates, or a vendor-specific code.

Common situations: Writing tests that simulate ACME server errors with an invented or misspelled code; handling a CA-specific error type not in the library's catalogue; upgrading servers that emit new error types while the client library is older.

Related errors


AI-assisted analysis of certbot/certbot@2b817be146 (2026-08-27). Data as JSON: /api/errors/c47aaaddd25705ef. Report an issue: GitHub.