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
- Fix the plugin attribute to use 'shortName@version' format (e.g., 'git@4.0.0').
- Remove malformed plugin attributes from config.xml if they are not needed.
- 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
- Use 'shortName@version' format in all plugin attributes.
- Validate config.xml plugin attributes before loading or committing.
- Avoid manual edits that break the @ separator.
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse XML
- Ambiguous plugin: {0}
- No such plugin: {0}
- No such update center: {0}
- {0} is not a Jenkins plugin
AI-assisted analysis of jenkinsci/jenkins@2e228ff40b (2026-08-14).
Data as JSON: /api/errors/e1fdc71ba0e22b42.
Report an issue: GitHub.