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.

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.

Appendix: 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)