apache/shenyu · error · IllegalArgumentException

Invalid upstream URL, expected 'host:port' format but was:

Error message

Invalid upstream URL, expected 'host:port' format but was: 

What it means

DiscoveryTransfer.mapToDiscoveryUpstreamData converts a CommonUpstream into DiscoveryUpstreamData and requires upstreamUrl in 'host:port' form. Splitting on ':' must yield at least 2 parts; if it doesn't (no colon or null already handled earlier), IllegalArgumentException is thrown.

Solutions

  1. Register the upstream with an explicit 'host:port' URL, e.g. '192.168.1.10:8080'
  2. Fix the source discovery data (nacos/zk/etcd instance metadata) to include the port
  3. If using IPv6, wrap the address in brackets with the port: '[::1]:8080' or pre-normalize before transfer
  4. Inspect CommonUpstream producers to ensure they always populate a port-bearing upstreamUrl

Example fix

// before
commonUpstream.setUpstreamUrl("my-service");
// after
commonUpstream.setUpstreamUrl("my-service:8080");
Defensive patterns

Strategy: validation

Validate before calling

boolean validUpstream = url != null && url.matches("[^:]+:\\d{1,5}");

Type guard

boolean isHostPort(String url) { int i = url.indexOf(':'); return i > 0 && i < url.length() - 1 && url.substring(i + 1).chars().allMatch(Character::isDigit); }

Try / catch

try { transfer.mapToDiscoveryUpstreamData(upstream); } catch (IllegalArgumentException e) { log.error("bad upstream {}", upstream.getUpstreamUrl(), e); skipEntry(); }

Prevention

When it happens

Trigger: A discovery upstream URL registered without a port — e.g. 'myhost' or 'http://myhost' style URLs where the split on ':' (limit 2) produces fewer than 2 parts — reaches mapToDiscoveryUpstreamData during discovery config sync/transfer.

Common situations: Registering backend instances without explicit ports in the dashboard or via client auto-registration; zookeeper/nacos/etcd discovery nodes storing bare hostnames; IPv6 addresses (multiple colons) breaking the naive split; upstream entries created before a format change in a newer ShenYu version.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12). Data as JSON: /api/errors/52f1b546249f14b6. Report an issue: GitHub.

Appendix: source

Thrown at shenyu-admin/src/main/java/org/apache/shenyu/admin/transfer/DiscoveryTransfer.java:356

            discoveryUpstreamDTO.setDateCreated(data.getDateCreated());
            discoveryUpstreamDTO.setDateUpdated(data.getDateUpdated());
            return discoveryUpstreamDTO;
        }).orElse(null);
    }

    /**
     * mapToDiscoveryUpstreamData.
     *
     * @param commonUpstream commonUpstream
     * @return DiscoveryUpstreamData
     */
    public DiscoveryUpstreamData mapToDiscoveryUpstreamData(CommonUpstream commonUpstream) {
        String upstreamUrl = commonUpstream.getUpstreamUrl();
        String[] parts = Optional.ofNullable(upstreamUrl)
                .map(url -> url.split(":", 2))
                .orElseThrow(() -> new IllegalArgumentException("Upstream URL must not be null"));
        if (parts.length < 2) {
            throw new IllegalArgumentException("Invalid upstream URL, expected 'host:port' format but was: " + upstreamUrl);
        }
        String host = parts[0];
        int port;
        try {
            port = Integer.parseInt(parts[1]);
        } catch (NumberFormatException ex) {
            throw new IllegalArgumentException("Invalid port in upstream URL: " + upstreamUrl, ex);
        }
        DiscoveryUpstreamDTO discoveryUpstreamDTO = CommonUpstreamUtils.buildDefaultDiscoveryUpstreamDTO(
                host,
                port,
                commonUpstream.getProtocol(),
                commonUpstream.getNamespaceId());
        Properties properties = Optional.ofNullable(discoveryUpstreamDTO.getProps())
                .map(props -> GsonUtils.getInstance().fromJson(props, Properties.class))
                .orElse(new Properties());
        properties.setProperty("healthCheckEnabled", String.valueOf(commonUpstream.isHealthCheckEnabled()));
        discoveryUpstreamDTO.setProps(GsonUtils.getInstance().toJson(properties));

View on GitHub (pinned to 567142e072)