quarkusio/quarkus · error · IllegalStateException
Invalid tag: {{entry}} (expected key=value)
Error message
Invalid tag: {{entry}} (expected key=value) What it means
VirtualThreadCollector parses JVM virtual-thread event tags from string entries and expects each entry in 'key=value' form. When a split on '=' does not yield exactly two parts, the collector cannot build a Micrometer Tag and throws IllegalStateException to fail fast rather than emit a malformed metric tag.
Source
Thrown at extensions/micrometer/runtime/src/main/java/io/quarkus/micrometer/runtime/binder/virtualthreads/VirtualThreadCollector.java:83
*
* @param tags the tags.
* @return the binder, {@code null} if the instantiation failed.
*/
public MeterBinder instantiate(List<Tag> tags) {
try {
Class<?> clazz = Class.forName(VIRTUAL_THREAD_BINDER_CLASSNAME);
return (MeterBinder) clazz.getDeclaredConstructor(Iterable.class).newInstance(tags);
} catch (Exception e) {
throw new IllegalStateException("Failed to instantiate " + VIRTUAL_THREAD_BINDER_CLASSNAME, e);
}
}
private Tag createTagFromEntry(String entry) {
String[] parts = entry.trim().split("=");
if (parts.length == 2) {
return Tag.of(parts[0], parts[1]);
} else {
throw new IllegalStateException("Invalid tag: " + entry + " (expected key=value)");
}
}
public MeterBinder getBinder() {
return binder;
}
public List<Tag> getTags() {
return tags;
}
public void init(@Observes StartupEvent event) {
if (enabled && binder != null) {
binder.bindTo(registry);
}
}
public void close(@Observes ShutdownEvent event) {View on GitHub (pinned to e1c734241f)
Solutions
- Ensure each entry contains exactly one '=' separating a non-empty key and value
- Trim the entry before passing it (the method trims internally, but '=b' or 'a=' still fails)
- Filter out malformed entries before building tags, e.g. entries.stream().filter(e -> e.indexOf('=')>0)
- Log or skip unparseable entries instead of passing them to the collector
Example fix
// before
Tag tag = createTagFromEntry("threadName-12"); // throws
// after
String entry = "threadName-12";
if (entry.indexOf('=') > 0) {
Tag tag = createTagFromEntry(entry);
} else {
// skip or log
} Defensive patterns
Strategy: validation
Validate before calling
boolean isValidTagEntry(String entry) {
String t = entry == null ? "" : entry.trim();
int i = t.indexOf('=');
return i > 0 && i < t.length() - 1 && t.indexOf('=', i + 1) == -1;
} Type guard
boolean isWellFormedKeyValue(String s) {
return s != null && s.trim().split("=").length == 2 && s.contains("=");
} Try / catch
try {
Tag tag = createTagFromEntry(entry);
} catch (IllegalStateException e) {
log.warnf("Skipping malformed tag entry: %s", entry);
} Prevention
- Validate entries contain exactly one '=' before feeding the collector
- Trim and normalize label sources before tag creation
- Write a unit test covering keyOnly, a=b=c and empty-value inputs
When it happens
Trigger: Calling createTagFromEntry (directly or via the collector's binder) with an entry string that has no '=', more than one '=', or an empty value such as 'keyOnly', 'a=b=c', or '=value'.
Common situations: Feeding custom or whitespace-padded event labels into the virtual-thread metrics collector; passing entries copied from a different format (e.g. JSON 'key: value'); manual tag construction in tests.
Related errors
- Failed to instantiate {{VIRTUAL_THREAD_BINDER_CLASSNAME}}
- Unexpected end of input: ${str}
- Unexpected character '${str.charAt(pos)}' at position ${pos}
- unbalanced quotes in
- invalid config ${comment}
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/95c6320530a96910.
Report an issue: GitHub.