{"record":{"id":"5180d5ce92b8f255","repo":"louislam/uptime-kuma","slug":"domain-expiry-unsupported-is-icann","errorCode":null,"errorMessage":"domain_expiry_unsupported_is_icann","messagePattern":"domain_expiry_unsupported_is_icann","errorType":"validation","errorClass":"TranslatableError","httpStatus":null,"severity":"warning","filePath":"server/model/domain_expiry.js","lineNumber":225,"sourceCode":"     * @param {Monitor} monitor Monitor object\n     * @throws {TranslatableError} Throws an error if the monitor type is unsupported or missing target.\n     * @returns {Promise<{ domain: string, tld: string }>} Domain expiry support info\n     */\n    static async checkSupport(monitor) {\n        if (!(monitor.type in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD)) {\n            throw new TranslatableError(\"domain_expiry_unsupported_monitor_type\");\n        }\n        const targetField = TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD[monitor.type];\n        const target = monitor[targetField];\n        if (typeof target !== \"string\" || target.length === 0) {\n            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,","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/server/model/domain_expiry.js#L207-L243","documentation":"After confirming a non-empty target string, checkSupport runs tldts.parse on it and checks tld.isIcann. If the suffix is not on the ICANN list (private TLDs like .local, .test, .internal, .home, or suffixes from a private network), this TranslatableError is thrown with domain/publicSuffix in meta for the frontend to interpolate.","triggerScenarios":"An http monitor pointed at an internal name like http://myapp.local, http://grafana.internal, http://service.test, or an IP address where tldts cannot find an ICANN suffix. Any hostname whose public suffix is not in the Public Suffix List as ICANN-managed.","commonSituations":"Internal/home-lab monitoring of LAN services; monitoring by raw IP; private TLDs used for service discovery; test environments using .test or .example.","solutions":["Use a real ICANN-registered domain for the monitor if you want expiry tracking (e.g. point the monitor at the public name even if it resolves internally).","If you can only monitor an internal name, accept that domain-expiry is not applicable — disable the domain-expiry option for that monitor.","Confirm the URL is parsed as you expect with `require('tldts').parse('http://myapp.local')` and inspect .isIcann and .publicSuffix.","Do not try to fake an ICANN suffix; the underlying data simply has no expiry date."],"exampleFix":"// before\n// monitor.url = \"http://grafana.local\"\nawait DomainExpiry.checkSupport(monitor); // throws domain_expiry_unsupported_is_icann\n\n// after — monitor a real registered domain instead\n// monitor.url = \"http://grafana.example.com\"","handlingStrategy":"validation","validationCode":"const { parse: parseTld } = require(\"tldts\");\nfunction isIcannDomain(target) {\n    const tld = parseTld(target);\n    return tld.isIcann === true;\n}","typeGuard":"function hasIcannSuffix(target) {\n    return parseTld(target).isIcann === true;\n}","tryCatchPattern":"try {\n    await DomainExpiry.checkSupport(monitor);\n} catch (e) {\n    if (e.msgi18n && e.message === \"domain_expiry_unsupported_is_icann\") {\n        // domain is internal/private — disable the expiry option\n    }\n}","preventionTips":["Reserve domain-expiry tracking for real ICANN domains; internal names have no expiry.","If you monitor a private name, do not enable the domain-expiry feature for that monitor.","Test the URL with tldts in a scratch script before enabling the feature."],"tags":["domain-expiry","validation","dns","i18n"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}