louislam/uptime-kuma · warning · TranslatableError

domain_expiry_unsupported_monitor_type

Error message

domain_expiry_unsupported_monitor_type

What it means

DomainExpiry.checkSupport(monitor) throws this TranslatableError when monitor.type is not a key in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD (src/util.ts:778). Supported types are: http, keyword, json-query, real-browser, websocket-upgrade (via 'url'), port, ping, dns, smtp, snmp (via 'hostname'), and grpc-keyword (via 'grpcUrl'). Because it extends TranslatableError, the frontend treats the message as a translation key (msgi18n=true) rather than a literal string.

Source

Thrown at server/model/domain_expiry.js:213

    parseName() {
        return parseTld(this.domain);
    }

    /**
     * @returns {(null|object)} parsed domain tld
     */
    get tld() {
        return this.parseName().publicSuffix;
    }

    /**
     * @param {Monitor} monitor Monitor object
     * @throws {TranslatableError} Throws an error if the monitor type is unsupported or missing target.
     * @returns {Promise<{ domain: string, tld: string }>} Domain expiry support info
     */
    static async checkSupport(monitor) {
        if (!(monitor.type in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD)) {
            throw new TranslatableError("domain_expiry_unsupported_monitor_type");
        }
        const targetField = TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD[monitor.type];
        const target = monitor[targetField];
        if (typeof target !== "string" || target.length === 0) {
            throw new TranslatableError("domain_expiry_unsupported_missing_target");
        }

        const tld = parseTld(target);

        // It must be checked first, filter out non-ICANN domains.
        if (!tld.isIcann) {
            throw new TranslatableError("domain_expiry_unsupported_is_icann", {
                // If domain is null, use hostname as fallback for better error message.
                domain: tld.domain ?? tld.hostname ?? "EMPTY DOMAIN",
                publicSuffix: tld.publicSuffix,
            });
        }

View on GitHub (pinned to 6b5ea01557)

Solutions

  1. Switch the monitor to a supported type if you genuinely need domain-expiry tracking (http/keyword/json-query/port/ping/dns/smtp/snmp/grpc-keyword/real-browser/websocket-upgrade).
  2. If you are developing a new monitor type that has a hostname, add it to TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD in src/util.ts with the correct field name and recompile.
  3. In the UI, the domain-expiry option is hidden for unsupported types — if you are seeing this from the API, reproduce via the UI to confirm support status.
  4. Catch TranslatableError at the call site and show the i18n key to the user instead of crashing.

Example fix

// before
if (!(monitor.type in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD)) {
    throw new TranslatableError("domain_expiry_unsupported_monitor_type");
}

// after — when adding a new type, extend the map first
// src/util.ts
// exports.TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD["my-type"] = "hostname";
Defensive patterns

Strategy: type-guard

Validate before calling

const { TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD } = require("../src/util");
function assertMonitorSupportsDomainExpiry(monitor) {
    if (!(monitor.type in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD)) {
        throw new Error(`Monitor type '${monitor.type}' does not support domain-expiry tracking`);
    }
}

Type guard

function monitorSupportsDomainExpiry(monitor) {
    return monitor?.type in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD;
}

Try / catch

try {
    await DomainExpiry.checkSupport(monitor);
} catch (e) {
    if (e.msgi18n && e.message === "domain_expiry_unsupported_monitor_type") {
        // hide/disable the domain-expiry option in the UI; not an error condition
    }
}

Prevention

When it happens

Trigger: Calling checkSupport with a monitor whose type is one of the unsupported kinds — push, docker, gamedig, mongodb, mysql, mqtt, radius, kahoot, selenium, ntfy, icmp-ping, dns-ping, steam-db, or any future type not in the map. The EditMonitor.vue gates the UI on the same map, so reaching this error usually means a direct API call or a type that was added without updating the map.

Common situations: Adding a new monitor type to the codebase but forgetting to extend TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD; a custom plugin monitor type; calling the domain-expiry API endpoint with an arbitrary monitor id whose type is unsupported.

Related errors


AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12). Data as JSON: /api/errors/a25e91f39124314d. Report an issue: GitHub.