You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
report() throws when the tags map fails while being read #37
report(String, Double, Map) can throw into the host program. The README and the class Javadoc both promise that it never does.
Where:src/main/java/software/stephenson/trace/TraceClient.java, report. The method has no try/catch. On the caller's thread it runs withVersion(tags, version), which copies the caller's map with new LinkedHashMap<>(tags), and then json(...), which casts each tag to String in quote(...). Every other path in the client catches IOException | RuntimeException, but this one is outside any such guard.
Two reproductions, both confirmed by a failing test on main (79bfa82):
A map that another thread is changing. The copy iterates the map, and a HashMap changed during the iteration throws ConcurrentModificationException. The exception escapes from withVersion, called at TraceClient.java:794:
java.util.ConcurrentModificationException
at java.util.LinkedHashMap.<init>
at software.stephenson.trace.TraceClient.withVersion(TraceClient.java:728)
at software.stephenson.trace.TraceClient.report(TraceClient.java:794)
A raw map holding a non-String value. A Bukkit ConfigurationSection.getValues(false) is a Map<String, Object>, so one unchecked cast puts an Integer where json() needs a String:
java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
Why it matters: "Never throws" is one of the properties the client exists to provide. A plugin that reports from a command handler or the server thread gets a stack trace from a usage report, which is exactly the outcome the comment in send() rules out.
Proposed fix: catch RuntimeException around the part of report that reads the caller's map, and drop that report. No log line is written, because this runs on the host's thread and a Handler may do IO there. The two existing drops on that path (a full queue, too many counted events) are silent for the same reason. The wire format, the opt-outs and the bounds are unchanged.
Activity