quarkusio/quarkus · error · java.lang.IllegalArgumentException

PURL type contains invalid character '

Error message

PURL type contains invalid character '

What it means

validateType() rejects any character after the first one that is not an ASCII letter, digit, '.', '+' or '-'. This keeps PURL types compliant with the spec's type grammar. The offending character is reported in the message along with the full type.

Source

Thrown at independent-projects/bootstrap/app-model/src/main/java/io/quarkus/sbom/Purl.java:601

        for (char c = '0'; c <= '9'; c++) {
            UNRESERVED[c] = true;
        }
        UNRESERVED['-'] = true;
        UNRESERVED['.'] = true;
        UNRESERVED['_'] = true;
        UNRESERVED['~'] = true;
    }

    private static void validateType(String type) {
        char first = type.charAt(0);
        if (!((first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z'))) {
            throw new IllegalArgumentException("PURL type must start with a letter: " + type);
        }
        for (int i = 1; i < type.length(); i++) {
            char c = type.charAt(i);
            if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
                    || (c >= '0' && c <= '9') || c == '.' || c == '+' || c == '-')) {
                throw new IllegalArgumentException("PURL type contains invalid character '" + c + "': " + type);
            }
        }
    }

    private static boolean isUnreserved(int c) {
        return c >= 0 && c < 128 && UNRESERVED[c];
    }

    public static class Builder {

        private String type;
        private String namespace;
        private String name;
        private String version;
        private TreeMap<String, String> qualifiers;
        private String subpath;

        private Builder() {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Replace invalid characters in the type: use '-', '.', or '+' where allowed, e.g. 'my-type' not 'my_type'
  2. Normalize the ecosystem name to the canonical PURL type (lowercase, letters/digits/./+/- only) before constructing
  3. Make sure only the segment between 'pkg:' and the first '/' is passed as the type, not a longer path

Example fix

// before
Purl p = Purl.parse("pkg:my_type/app@1.0");
// after
Purl p = Purl.parse("pkg:my-type/app@1.0");
Defensive patterns

Strategy: validation

Validate before calling

static boolean isValidPurlType(String type) {
    if (type == null || type.isEmpty()) return false;
    return type.matches("[a-zA-Z][a-zA-Z0-9.+-]*");
}

Type guard

String normalizeType(String raw) {
    return raw == null ? null : raw.replaceAll("[^a-zA-Z0-9.+-]", "-");
}

Try / catch

try {
    return Purl.parse(purlString);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("PURL type contains invalid character")) {
        log.errorf("Illegal character in PURL type: %s", purlString);
    }
    throw e;
}

Prevention

When it happens

Trigger: Constructing or parsing a Purl whose type contains characters like '_', '/', ' ', ':', or non-ASCII letters after the first character, e.g. 'pkg:my_type/app' or 'pkg:foo bar/baz'.

Common situations: Using underscore-separated ecosystem names ('go_module' instead of 'gomodule'); passing a whole path or URL fragment instead of just the type; embedding spaces from split coordinates or log output.

Understand the failure class

Related errors


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