jenkinsci/jenkins · error · SAXException

Malformed plugin attribute: {0}

Error message

Malformed plugin attribute: {0}

What it means

SAXException thrown when parsing config.xml and encountering a 'plugin' attribute that does not match the pattern [^@]+@[^@]+ (i.e., must contain exactly one '@' separating short name and version, with no additional '@' characters). This validates dependency declarations embedded in XML configuration attributes.

Source

Thrown at core/src/main/java/hudson/PluginManager.java:2311

    }

    /**
     * Parses configuration XML files and picks up references to XML files.
     */
    public Map<String, VersionNumber> parseRequestedPlugins(InputStream configXml) throws IOException {
        final Map<String, VersionNumber> requestedPlugins = new TreeMap<>();
        try {
            SAXParserFactory spf = SAXParserFactory.newInstance();
            spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
            spf.newSAXParser().parse(configXml, new DefaultHandler() {
                @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
                    String plugin = attributes.getValue("plugin");
                    if (plugin == null) {
                        return;
                    }
                    if (!plugin.matches("[^@]+@[^@]+")) {
                        throw new SAXException("Malformed plugin attribute: " + plugin);
                    }
                    int at = plugin.indexOf('@');
                    String shortName = plugin.substring(0, at);
                    VersionNumber existing = requestedPlugins.get(shortName);
                    VersionNumber requested = new VersionNumber(plugin.substring(at + 1));
                    if (existing == null || existing.compareTo(requested) < 0) {
                        requestedPlugins.put(shortName, requested);
                    }
                }

                @Override public InputSource resolveEntity(String publicId, String systemId) throws IOException,
                        SAXException {
                    return RestrictiveEntityResolver.INSTANCE.resolveEntity(publicId, systemId);
                }

            });
        } catch (SAXException x) {
            throw new IOException("Failed to parse XML", x);

View on GitHub (pinned to 2e228ff40b)

Solutions

  1. Fix the plugin attribute to use 'shortName@version' format (e.g., 'git@4.0.0').
  2. Remove malformed plugin attributes from config.xml if they are not needed.
  3. Validate config.xml against the expected format before loading.

Example fix

<!-- before -->
<builder class="hudson.tasks.Shell" plugin="git"/>

<!-- after -->
<builder class="hudson.tasks.Shell" plugin="git@4.0.0"/>
Defensive patterns

Strategy: validation

Validate before calling

private static final Pattern PLUGIN_ATTR = Pattern.compile("[^@]+@[^@]+");
public static boolean isValidPluginAttribute(String attr) {
    return attr != null && PLUGIN_ATTR.matcher(attr).matches();
}

Type guard

public static boolean isValidPluginAttribute(String attr) {
    if (attr == null) return false;
    int count = 0;
    for (char c : attr.toCharArray()) if (c == '@') count++;
    return count == 1;
}

Try / catch

try {
    Map<String, VersionNumber> deps = pluginManager.parseRequestedPlugins(configXml);
} catch (IOException e) {
    if (e.getCause() != null && e.getCause().getMessage().contains("Malformed plugin attribute")) {
        listener.error("Config XML has malformed plugin attribute; expected 'name@version'.");
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: A plugin attribute in config.xml (e.g., plugin='foo' or plugin='foo@1.0@beta') that lacks the required 'shortName@version' format — either no '@', or more than one '@'.

Common situations: Manually edited config.xml with malformed plugin attributes, a plugin that writes incorrect dependency specifiers, or migration from an older format that used a different separator.

Understand the failure class

Related errors


AI-assisted analysis of jenkinsci/jenkins@2e228ff40b (2026-08-14). Data as JSON: /api/errors/e1fdc71ba0e22b42. Report an issue: GitHub.