oracle/graal · error · RuntimeException

boxed object: {} is not a primitive

Error message

boxed object: {} is not a primitive

What it means

Thrown by TagConstants.getTagFromPrimitive(Object boxed) when the argument is none of the eight primitive wrappers (Integer, Float, Double, Long, Byte, Short, Character, Boolean). The JDWP protocol assigns a one-byte tag to every value it serializes, and this helper maps a boxed primitive to its tag; a non-primitive object has no primitive tag, so the mapping is a caller bug, not an environmental condition.

Source

Thrown at espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/TagConstants.java:84

        if (boxed instanceof Double) {
            return DOUBLE;
        }
        if (boxed instanceof Long) {
            return LONG;
        }
        if (boxed instanceof Byte) {
            return BYTE;
        }
        if (boxed instanceof Short) {
            return SHORT;
        }
        if (boxed instanceof Character) {
            return CHAR;
        }
        if (boxed instanceof Boolean) {
            return BOOLEAN;
        }
        throw new RuntimeException("boxed object: " + boxed.getClass() + " is not a primitive");
    }

    public static byte toTagConstant(JavaKind kind) {
        switch (kind) {
            case Boolean:
                return TagConstants.BOOLEAN;
            case Byte:
                return TagConstants.BYTE;
            case Short:
                return TagConstants.SHORT;
            case Char:
                return TagConstants.CHAR;
            case Int:
                return TagConstants.INT;
            case Float:
                return TagConstants.FLOAT;
            case Long:
                return TagConstants.LONG;

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Branch on the value first: use getTagFromPrimitive only when value instanceof Number, Character, or Boolean
  2. Handle reference values with the OBJECT/STRING/ARRAY/THREAD tags before calling the primitive mapper
  3. Null-check the boxed value before mapping

Example fix

// before
byte tag = TagConstants.getTagFromPrimitive(value);

// after
byte tag;
if (value == null || !(value instanceof Number || value instanceof Character || value instanceof Boolean)) {
    tag = TagConstants.OBJECT;
} else {
    tag = TagConstants.getTagFromPrimitive(value);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(boxed instanceof Number || boxed instanceof Character || boxed instanceof Boolean)) {
    // route to reference-value tag handling instead
}

Type guard

static boolean isBoxedPrimitive(Object v) {
    return v instanceof Number || v instanceof Character || v instanceof Boolean;
}

Try / catch

catch (RuntimeException e) when message contains 'not a primitive': map value to TagConstants.OBJECT and continue serialization.

Prevention

When it happens

Trigger: A JDWP back-end code path (e.g. building a Value packet for a watched field or method return) calls getTagFromPrimitive on a value that is actually a String, an array, null, or a plain object; null fails earlier at boxed.getClass() with an NPE if not guarded.

Common situations: Debugger-plugin code in Espresso that assumes a static field type guarantees a primitive value (obfuscated/recycled classes where the declared type lies), or new JDWP command implementations routing reference values through the primitive mapper.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/e985fc6a538ee149. Report an issue: GitHub.