Tencent/matrix · error · IllegalArgumentException

acceptHeapDumpRecord loop with unknown tag " + heapDumpTag…

Error message

acceptHeapDumpRecord loop with unknown tag " + heapDumpTag + " with " + mStreamIn.available() + " bytes possibly remaining

What it means

HprofReader.acceptHeapDumpRecord() switches on each heap dump record's tag. When the tag does not match any known HprofConstants record type, the parser throws IllegalArgumentException reporting the unknown tag and remaining bytes, because the record layout is unknown and the stream position can no longer be advanced safely.

Solutions

  1. Upgrade Matrix so HprofConstants/acceptHeapDumpRecord covers the record tags of the runtime that produced the dump.
  2. Capture the hprof with the standard 'am dumpheap' of the target device and re-check the dump format version in the header.
  3. Fix any earlier parse error (e.g. bad idSize) that may have desynchronized the byte stream.
  4. Catch IllegalArgumentException around accept() and treat the dump as unsupported rather than crashing.
Defensive patterns

Strategy: try-catch

Validate before calling

String version = readHprofVersionHeader(file);
if (!SUPPORTED_VERSIONS.contains(version)) {
    throw new IOException("unsupported hprof version: " + version);
}

Try / catch

try {
    hprofReader.accept(visitor);
} catch (IllegalArgumentException e) {
    Log.e(TAG, "unknown heap dump tag; hprof likely from newer runtime", e);
}

Prevention

When it happens

Trigger: Parsing an hprof whose heap dump section contains a tag outside the parser's known set — dumps written by a newer ART/JVM HPROF version, or a stream already desynchronized from earlier mis-parsing.

Common situations: Hprof from a newer Android/JDK version than the parser supports (e.g. Android 14+ ART adding record types); parsing the 1.0.2 variant with a 1.0.1 parser; a prior bad idSize silently desynchronizing the stream.

Related errors


AI-assisted analysis of Tencent/matrix@3b8293bd65 (2026-09-08). Data as JSON: /api/errors/91e11f91700123cc. 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/HprofReader.java:222

                    length -= mIdSize;
                    break;
                case HprofConstants.HEAPDUMP_ROOT_REFERENCE_CLEANUP:
                    hdv.visitHeapDumpBasicObj(heapDumpTag, IOUtil.readID(mStreamIn, mIdSize));
                    length -= mIdSize;
                    break;
                case HprofConstants.HEAPDUMP_ROOT_VM_INTERNAL:
                    hdv.visitHeapDumpBasicObj(heapDumpTag, IOUtil.readID(mStreamIn, mIdSize));
                    length -= mIdSize;
                    break;
                case HprofConstants.HEAPDUMP_ROOT_JNI_MONITOR:
                    length -= acceptJniMonitor(hdv);
                    break;
                case HprofConstants.HEAPDUMP_ROOT_UNREACHABLE:
                    hdv.visitHeapDumpBasicObj(heapDumpTag, IOUtil.readID(mStreamIn, mIdSize));
                    length -= mIdSize;
                    break;
                default:
                    throw new IllegalArgumentException(
                            "acceptHeapDumpRecord loop with unknown tag " + heapDumpTag
                                    + " with " + mStreamIn.available()
                                    + " bytes possibly remaining");
            }
        }
        hdv.visitEnd();
    }

    private void acceptUnconcernedRecord(int tag, int timestamp, long length, HprofVisitor hv) throws IOException {
        final byte[] data = new byte[(int) length];
        IOUtil.readFully(mStreamIn, data, 0, length);
        hv.visitUnconcernedRecord(tag, timestamp, length, data);
    }

    private int acceptHeapDumpInfo(HprofHeapDumpVisitor hdv) throws IOException {
        final int heapId = IOUtil.readBEInt(mStreamIn);
        final ID heapNameId = IOUtil.readID(mStreamIn, mIdSize);
        hdv.visitHeapDumpInfo(heapId, heapNameId);

View on GitHub (pinned to 3b8293bd65)