Anuken/Mindustry · error · RuntimeException

Invalid array size

Error message

Invalid array size: {}

What it means

TypeIO.readObject case 6 (IntSeq) reads a short length and rejects it if it exceeds maxArraySize (1000, or 200 in unsafe contexts). This is a server-side sanity check against malicious or corrupt packets claiming an oversized IntSeq. Plain RuntimeException, not ArcRuntimeException.

Solutions

  1. Ensure the client respects the same maxArraySize caps when writing (writeObject already enforces 1000).
  2. Treat this as a protocol violation: disconnect/log the offending client rather than retrying.
  3. If developing a mod, never hand-craft TypeIO bytes; use writeObject/readObject pairs.

Example fix

// before (client)
write.s((short)5000); // bypasses writeObject; server read throws

// after
TypeIO.writeObject(write, intSeq); // writeObject enforces the 1000 cap
// server: catch RuntimeException and kick the offending connection
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

try {
    Object o = TypeIO.readObject(read);
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Invalid array size")) {
        // protocol violation; disconnect offending client
    }
}

Prevention

When it happens

Trigger: A client packet or save region where the IntSeq length short is greater than the effective maxArraySize; in safe contexts the limit is 1000, in non-safe (build-plan) contexts it is 200.

Common situations: A malicious/corrupt client sending an inflated length to force a large allocation; a version drift that misreads bytes as a length.

Related errors


AI-assisted analysis of Anuken/Mindustry@f695ad7e60 (2026-08-14). Data as JSON: /api/errors/c602e6cba71264bb. Report an issue: GitHub.

Appendix: source

Thrown at core/src/mindustry/io/TypeIO.java:203

        return switch(type){
            case 0 -> null;
            case 1 -> read.i();
            case 2 -> read.l();
            case 3 -> read.f();
            case 4 -> {
                byte exists = read.b();
                if(exists != 0){
                    //in a safe context, strings can only be 1200 chars
                    yield read.str(safe ? 1200 : 0);
                }else{
                    yield null;
                }
            }
            case 5 -> mapper == null ? content.getByID(ContentType.all[read.b()], read.s()) : mapper.get(ContentType.all[read.b()], read.s());
            case 6 -> {
                if(!allowArrays) throw new RuntimeException("Nested arrays are not allowed");
                short len = read.s();
                if(len > maxArraySize) throw new RuntimeException("Invalid array size: " + len);
                IntSeq arr = new IntSeq(len);
                for(int i = 0; i < len; i ++) arr.add(read.i());
                yield arr;
            }
            case 7 -> new Point2(read.i(), read.i());
            case 8 -> {
                if(!allowArrays) throw new RuntimeException("Nested arrays are not allowed");
                int len = read.ub();
                Point2[] out = new Point2[len];
                for(int i = 0; i < len; i ++) out[i] = Point2.unpack(read.i());
                yield out;
            }
            case 9 -> content.<UnlockableContent>getByID(ContentType.all[read.b()], read.s()).techNode;
            case 10 -> read.bool();
            case 11 -> read.d();
            case 12 -> !box ? world.build(read.i()) : new BuildingBox(read.i());
            case 13 -> LAccess.all[read.s()];
            case 14 -> {

View on GitHub (pinned to f695ad7e60)