quarkusio/quarkus · error · RuntimeException

Invalid external URL, can not be empty

Error message

Invalid external URL, can not be empty

What it means

ExternalPageBuilder.url(String, String) validates that the external page URL is non-null and non-empty before storing it as the EXTERNAL_URL metadata. An external Dev UI page is by definition a link to some URL; an empty one cannot be rendered.

Source

Thrown at extensions/devui/deployment-spi/src/main/java/io/quarkus/devui/spi/page/ExternalPageBuilder.java:35

    public static final String MIME_TYPE_HTML = "text/html";
    public static final String MIME_TYPE_JSON = "application/json";
    public static final String MIME_TYPE_YAML = "application/yaml";
    public static final String MIME_TYPE_PDF = "application/pdf";

    protected ExternalPageBuilder(String title) {
        super();
        super.title = title;
        super.componentLink = QWC_EXTERNAL_PAGE_JS;
        super.internalComponent = true;// As external page runs on "internal" namespace
    }

    public ExternalPageBuilder url(String url) {
        return url(url, null);
    }

    public ExternalPageBuilder url(String url, String externalLink) {
        if (url == null || url.isEmpty()) {
            throw new RuntimeException("Invalid external URL, can not be empty");
        }
        super.metadata.put(EXTERNAL_URL, url);
        if (externalLink != null) {
            return staticLabel("<a style='color: var(--lumo-contrast-80pct);' href='" + externalLink
                    + "' target='_blank'><vaadin-icon class='icon' icon='font-awesome-solid:up-right-from-square'></vaadin-icon></a>");
        }
        return this;
    }

    @SuppressWarnings("unchecked")
    public ExternalPageBuilder dynamicUrlJsonRPCMethodName(String methodName) {
        if (methodName == null || methodName.isEmpty()) {
            throw new RuntimeException("Invalid dynamic URL Method name, can not be empty");
        }
        super.metadata.put(DYNAMIC_URL, methodName);
        return this;
    }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Pass a concrete absolute or app-relative URL, e.g. externalPage().url("https://grafana.example.com/dashboard")
  2. If the URL comes from config, validate/default it before building: url(cfg.url().isEmpty() ? DEFAULT_URL : cfg.url())
  3. Skip registering the external page when the configured URL is absent instead of building it with an empty value
  4. Check for leading/trailing whitespace-only values — normalize with strip() first

Example fix

// before
String url = config.dashboardUrl(); // ""
externalPage().url(url); // throws
// after
String url = config.dashboardUrl();
if (url != null && !url.isBlank()) {
    externalPage().url(url);
}
Defensive patterns

Strategy: validation

Validate before calling

if (url == null || url.isBlank()) {
    throw new IllegalStateException("External page URL must be non-empty before calling url()");
}

Type guard

boolean isUsableUrl(String u) {
    return u != null && !u.isBlank() && (u.startsWith("http://") || u.startsWith("https://") || u.startsWith("/"));
}

Try / catch

try {
    externalPage().url(u);
} catch (RuntimeException e) {
    if (e.getMessage().contains("Invalid external URL")) {
        log.warnf("Skipping external page, url empty");
    } else throw e;
}

Prevention

When it happens

Trigger: Calling externalPage().url("") or url(null) directly, or via the one-arg url(String) overload which delegates with a null externalLink.

Common situations: URL loaded from configuration (application.properties/env) that is missing or empty when building Dev UI pages at deployment time; template strings interpolated from config keys that were never set; typos so the variable holding the URL is a different, empty one.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/f8b8bd0bbc72408c. Report an issue: GitHub.