{"record":{"id":"755112a4b69b1b46","repo":"louislam/uptime-kuma","slug":"domain-expiry-unsupported-unsupported-tld-no-rdap","errorCode":null,"errorMessage":"domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint","messagePattern":"domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint","errorType":"validation","errorClass":"TranslatableError","httpStatus":null,"severity":"warning","filePath":"server/model/domain_expiry.js","lineNumber":236,"sourceCode":"            throw new TranslatableError(\"domain_expiry_unsupported_missing_target\");\n        }\n\n        const tld = parseTld(target);\n\n        // It must be checked first, filter out non-ICANN domains.\n        if (!tld.isIcann) {\n            throw new TranslatableError(\"domain_expiry_unsupported_is_icann\", {\n                // If domain is null, use hostname as fallback for better error message.\n                domain: tld.domain ?? tld.hostname ?? \"EMPTY DOMAIN\",\n                publicSuffix: tld.publicSuffix,\n            });\n        }\n\n        const publicSuffix = tld.publicSuffix;\n        const rootTld = publicSuffix.split(\".\").pop();\n        const rdap = await getRdapServer(publicSuffix);\n        if (!rdap) {\n            throw new TranslatableError(\"domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint\", {\n                publicSuffix,\n            });\n        }\n\n        return {\n            domain: tld.domain,\n            tld: rootTld,\n        };\n    }\n\n    /**\n     * @param {string} domainName Domain name\n     * @returns {Promise<DomainExpiry>} Domain expiry bean\n     */\n    static async findByDomainNameOrCreate(domainName) {\n        let domain = await DomainExpiry.findByName(domainName);\n        if (!domain && domainName) {\n            domain = await DomainExpiry.createByName(domainName);","sourceCodeStart":218,"sourceCodeEnd":254,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/server/model/domain_expiry.js#L218-L254","documentation":"checkSupport calls getRdapServer(publicSuffix), which looks up the root TLD in IANA's https://data.iana.org/rdap/dns.json services list. If no RDAP server is registered for that TLD, it returns null and this TranslatableError is thrown with publicSuffix in meta. Not every ICANN TLD operates an RDAP endpoint, so the domain can be valid yet unsupported for expiry lookups.","triggerScenarios":"A correctly-registered ICANN domain whose registry does not provide an RDAP server (some ccTLDs, some new gTLDs); the IANA RDAP list is stale or the cached entry in the settings table is empty; transient network failure fetching dns.json left a bad cache.","commonSituations":"Monitoring domains on smaller ccTLDs without RDAP coverage; air-gapped instances that cannot reach data.iana.org to refresh the list; an instance whose first fetch of dns.json failed and cached an empty services array.","solutions":["Confirm the TLD really has no RDAP by checking https://data.iana.org/rdap/dns.json directly — if the registry has since added RDAP, force a refresh.","Force a refresh of the cached RDAP data: clear the relevant Settings entry (the module caches for one week and falls back to offline data) and restart so getRdapDnsData() re-fetches.","If the TLD genuinely lacks RDAP, domain-expiry tracking is unavailable for that monitor; disable the option.","Verify outbound HTTPS to data.iana.org works from the Uptime Kuma host (proxy/firewall can block the refresh)."],"exampleFix":"// before\nconst rdap = await getRdapServer(publicSuffix);\nif (!rdap) {\n    throw new TranslatableError(\"domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint\", { publicSuffix });\n}\n\n// after — also expose the root TLD for easier user messaging\nthrow new TranslatableError(\"domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint\", {\n    publicSuffix,\n    rootTld: publicSuffix.split(\".\").pop()\n});","handlingStrategy":"validation","validationCode":"// Pre-check that an RDAP server exists for the TLD before enabling the feature\nconst { parse: parseTld } = require(\"tldts\");\nasync function hasRdap(target) {\n    const { publicSuffix } = parseTld(target);\n    // mirror the helper in domain_expiry.js\n    const data = await (await fetch(\"https://data.iana.org/rdap/dns.json\")).json();\n    const rootTld = publicSuffix?.split(\".\").pop();\n    return (data.services ?? []).some(([tlds]) => tlds.includes(rootTld));\n}","typeGuard":null,"tryCatchPattern":"try {\n    await DomainExpiry.checkSupport(monitor);\n} catch (e) {\n    if (e.msgi18n && e.message === \"domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint\") {\n        // TLD has no RDAP; expiry tracking is unavailable — disable it\n    }\n}","preventionTips":["Keep the cached IANA RDAP list fresh by ensuring outbound HTTPS to data.iana.org works.","For air-gapped instances, pre-seed the RDAP data via the settings table.","Educate operators that some TLDs simply lack RDAP coverage."],"tags":["domain-expiry","rdap","dns","validation","i18n"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}