stanfordnlp/CoreNLP · error · RuntimeException

Unknown object to serialize

Error message

Unknown object to serialize: ${value}

What it means

routeObject is JSONOutputter's type-dispatch: it walks through known Java types (String, Number, Boolean, Iterable, Map, arrays, etc.) and serializes each. If the value's class matches none of the branches — even after primitive boxing — a RuntimeException 'Unknown object to serialize' is thrown.

Solutions

  1. Convert the value to a JSON-friendly type (String, Number, Boolean, List, Map) before writing output
  2. Implement a custom JSONOutputter/Outputter that knows how to serialize your object type
  3. Remove or filter non-serializable annotation keys before calling JSONOutputter.write
  4. Prefer Objects.toString(value) if a plain string representation is acceptable

Example fix

// before
map.put("timestamp", new Date()); // RuntimeException
// after
map.put("timestamp", new Date().toInstant().toString());
Defensive patterns

Strategy: validation

Validate before calling

for (Object v : values) {
  if (!(v instanceof String || v instanceof Number || v instanceof Boolean
      || v instanceof Iterable || v instanceof Map
      || (v != null && v.getClass().isArray())))
    throw new IllegalArgumentException("Not JSON-serializable: " + v.getClass());
}

Type guard

static boolean isJsonSerializable(Object v) {
  return v == null || v instanceof String || v instanceof Number || v instanceof Boolean
      || v instanceof Iterable || v instanceof Map || v.getClass().isArray();
}

Try / catch

try {
  JSONOutputter.jsonPrint(writer, ann);
} catch (RuntimeException e) {
  if (e.getMessage().startsWith("Unknown object to serialize")) {
    // strip/convert custom keys and retry
  } else throw e;
}

Prevention

When it happens

Trigger: Placing an arbitrary object (custom class, URI, Date, CoreLabel, etc.) into an annotation value or map that JSONOutputter attempts to render; the object is not a String, Number, Boolean, Collection, Map, or array.

Common situations: Custom annotators storing rich Java objects under custom keys and then expecting JSONOutputter to dump the whole annotation; forgetting to convert objects like Date or custom feature structures to primitives/strings first.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of stanfordnlp/CoreNLP@1b7edd19c4 (2026-09-10). Data as JSON: /api/errors/00a03c171e2c858b. Report an issue: GitHub.

Appendix: source

Thrown at src/edu/stanford/nlp/pipeline/JSONOutputter.java:568

        writer.write(Boolean.toString((Boolean) value));
      } else if (int.class.isAssignableFrom(value.getClass())) {
        routeObject(indent, Integer.valueOf((int) value));
      } else if (short.class.isAssignableFrom(value.getClass())) {
        routeObject(indent, Short.valueOf((short) value));
      } else if (byte.class.isAssignableFrom(value.getClass())) {
        routeObject(indent, Byte.valueOf((byte) value));
      } else if (long.class.isAssignableFrom(value.getClass())) {
        routeObject(indent, Long.valueOf((long) value));
      } else if (char.class.isAssignableFrom(value.getClass())) {
        routeObject(indent, Character.valueOf((char) value));
      } else if (float.class.isAssignableFrom(value.getClass())) {
        routeObject(indent, Float.valueOf((float) value));
      } else if (double.class.isAssignableFrom(value.getClass())) {
        routeObject(indent, Double.valueOf((double) value));
      } else if (boolean.class.isAssignableFrom(value.getClass())) {
        routeObject(indent, Boolean.valueOf((boolean) value));
      } else {
        throw new RuntimeException("Unknown object to serialize: " + value);
      }
    }

    public void object(int indent, Consumer<Writer> callback) {
      writer.write("{");
      final Pointer<Boolean> firstCall = new Pointer<>(true);
      callback.accept((key, value) -> {
        if (key != null && value != null) {
          // First call overhead
          if (!firstCall.dereference().orElse(false)) {
            writer.write(",");
          }
          firstCall.set(false);
          // Write the key
          newline();
          indent(indent + 1);
          writer.write("\"");
          writer.write(StringUtils.escapeJsonString(key));

View on GitHub (pinned to 1b7edd19c4)