The problem
Requesting a certificate for example.com An unexpected error occurred: AttributeError: 'Meta' object has no attribute 'profiles'
The solution
Upgrade or reinstall py311-acme
Debugging
Check /var/log/letsencrypt/letsencrypt.log
for JSON dump of the Directory object:
DEBUG:acme.client:Sending GET request to https://acme-v02.api.letsencrypt.org/directory.
DEBUG:acme.client:Received response:
{
"keyChange": "https://acme-v02.api.letsencrypt.org/acme/key-change",
"meta": {
"caaIdentities": [
"letsencrypt.org"
],
"profiles": {
"classic": "https://letsencrypt.org/docs/profiles#classic",
"shortlived": "https://letsencrypt.org/docs/profiles#shortlived (not yet generally available)",
"tlsclient": "https://letsencrypt.org/docs/profiles#tlsclient",
"tlsserver": "https://letsencrypt.org/docs/profiles#tlsserver"
},
"termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.5-February-24-2025.pdf",
"website": "https://letsencrypt.org"
},
"newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct",
"newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce",
"newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order",
"renewalInfo": "https://acme-v02.api.letsencrypt.org/acme/renewal-info",
"revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert"
}
As you can see the profiles are there, yet the code does not see them.
Places to check: /usr/local/lib/python3.11/site-packages/certbot/_internal/client.py
print(directory.meta)
increate_acme_client()
where directory object is introducedprint(self.acme.directory.meta)
in_get_order_and_authorizations()
where the above is simply reused
If you're having this issue, your meta likely looks like this:
Meta(terms_of_service='https://letsencrypt.org/documents/LE-SA-v1.5-February-24-2025.pdf', website='https://letsencrypt.org', caa_identities=('letsencrypt.org',), external_account_required=None)
So check /usr/local/lib/python3.11/site-packages/acme/messages.py for class Directory
-> class Meta
-> profiles. Your copy likely does not have that piece.
class Directory(jose.JSONDeSerializable):
class Meta(jose.JSONObjectWithFields):
_terms_of_service: str = jose.field('termsOfService', omitempty=True)
website: str = jose.field('website', omitempty=True)
caa_identities: List[str] = jose.field('caaIdentities', omitempty=True)
external_account_required: bool = jose.field('externalAccountRequired', omitempty=True)
profiles: Dict[str, str] = jose.field('profiles', omitempty=True) # <- THIS