Tencent/matrix · error · IllegalArgumentException

bad value type

Error message

bad value type: ${value.getClass().getName()}

What it means

IOUtil.writeValue serializes a value to an hprof-backed output stream, supporting only Boolean, Character, Byte, Short, Integer and Long. If the value's runtime type is anything else (e.g. Float, Double, String, or an unexpected boxed object), it throws IllegalArgumentException. This is an internal type contract for hprof record value writing; the library only knows how to BE-encode those primitive widths.

Solutions

  1. Cast or convert the value to a supported type (Boolean, Character, Byte, Short, Integer, Long) before writing.
  2. If the value is Float/Double, convert via Float.floatToIntBits / Double.doubleToLongBits and write as int/long.
  3. For strings or complex data, use the hprof string/byte-array record APIs instead of writeValue.
  4. Check the calling record-building code for a type-mapping bug introduced by recent changes.

Example fix

// before
IOUtil.writeValue(out, 3.14f);
// after
IOUtil.writeBEInt(out, Float.floatToIntBits(3.14f));
Defensive patterns

Strategy: validation

Validate before calling

if (!(value instanceof Boolean || value instanceof Character || value instanceof Byte || value instanceof Short || value instanceof Integer || value instanceof Long)) {
    throw new IllegalArgumentException("Unsupported hprof value type: " + value.getClass());
}
IOUtil.writeValue(out, value);

Type guard

static boolean isWritableHprofValue(Object v) {
    return v instanceof Boolean || v instanceof Character || v instanceof Byte
        || v instanceof Short || v instanceof Integer || v instanceof Long;
}

Try / catch

try {
    IOUtil.writeValue(out, value);
} catch (IllegalArgumentException e) {
    Log.w(TAG, "Unsupported value type, skipping", e);
}

Prevention

When it happens

Trigger: Calling HprofBufferWriter/IOUtil.writeValue (directly or via hprof record building in matrix-resource-canary) with a value whose class is not one of the supported wrapper types, e.g. passing a Float, Double, String or custom object where a primitive boxed value is expected.

Common situations: Custom hprof record construction or patched/forked code writing new record types with unsupported value types; version drift where a record format expects a type the writer doesn't handle; reflection-based value extraction returning an unexpected boxed type.

Related errors


AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08). Data as JSON: /api/errors/d99602f78db11275. Report an issue: GitHub.

Appendix: source

Thrown at matrix/matrix-android/matrix-resource-canary/matrix-resource-canary-android/src/main/java/com/tencent/matrix/resource/hproflib/utils/IOUtil.java:264

            IOUtil.writeID(out, (ID) value);
        } else if (value instanceof Boolean || boolean.class.isAssignableFrom(value.getClass())) {
            out.write((boolean) value ? 1 : 0);
        } else if (value instanceof Character || char.class.isAssignableFrom(value.getClass())) {
            IOUtil.writeBEShort(out, (char) value);
        } else if (value instanceof Float || float.class.isAssignableFrom(value.getClass())) {
            IOUtil.writeBEInt(out, Float.floatToRawIntBits((Float) value));
        } else if (value instanceof Double || double.class.isAssignableFrom(value.getClass())) {
            IOUtil.writeBELong(out, Double.doubleToRawLongBits((Double) value));
        } else if (value instanceof Byte || byte.class.isAssignableFrom(value.getClass())) {
            out.write((byte) value);
        } else if (value instanceof Short || short.class.isAssignableFrom(value.getClass())) {
            IOUtil.writeBEShort(out, (short) value);
        } else if (value instanceof Integer || int.class.isAssignableFrom(value.getClass())) {
            IOUtil.writeBEInt(out, (int) value);
        } else if (value instanceof Long || long.class.isAssignableFrom(value.getClass())) {
            IOUtil.writeBELong(out, (long) value);
        } else {
            throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
        }
    }

    public static void skip(OutputStream out, long size) throws IOException {
        final byte[] emptyBuf = new byte[4096];
        for (int i = 0; i < (size >> 12); ++i) {
            out.write(emptyBuf);
        }
        out.write(emptyBuf, 0, (int) (size & ((1L << 12) - 1)));
    }

    private IOUtil() {
        throw new UnsupportedOperationException();
    }
}

View on GitHub (pinned to 3b8293bd65)