Skip to content

report() throws when the tags map fails while being read #37

Closed

Description

@dmccoystephenson

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

  1. 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)
    
  2. 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.

This issue was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions