{"record":{"id":"0463aac94db22539","repo":"Anuken/Mindustry","slug":"error-reading-region-read-length-mismatch-e","errorCode":null,"errorMessage":"Error reading region \"{}\": read length mismatch. Expected: {}; Actual: {}","messagePattern":"Error reading region \"(.+?)\": read length mismatch\\. Expected: (.+?); Actual: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"core/src/mindustry/io/SaveFileReader.java","lineNumber":97,"sourceCode":"    protected static final Reads chunkReads = new Reads(null);\n    protected static boolean chunkNested = false;\n\n    public static String mapFallback(String name){\n        return fallback.get(name, name);\n    }\n\n    //TODO: unify readRegion with readChunk, they do the same thing and both should have good error messages\n    public void readRegion(String name, DataInput stream, CounterInputStream counter, IORunner<DataInput> cons) throws IOException{\n        counter.resetCount();\n        int length;\n        try{\n            length = readChunk(stream, (chunkStream, len) -> cons.accept(chunkStream));\n        }catch(Throwable e){\n            throw new IOException(\"Error reading region \\\"\" + name + \"\\\".\", e);\n        }\n\n        if(length != counter.count - 4){\n            throw new IOException(\"Error reading region \\\"\" + name + \"\\\": read length mismatch. Expected: \" + length + \"; Actual: \" + (counter.count - 4));\n        }\n    }\n\n    public void writeRegion(String name, DataOutput stream, IORunner<DataOutput> cons) throws IOException{\n        try{\n            writeChunk(stream, writes -> cons.accept(writes.output));\n        }catch(Throwable e){\n            throw new IOException(\"Error writing region \\\"\" + name + \"\\\".\", e);\n        }\n    }\n\n    /** Write a chunk of input to the stream. An integer of some length is written first, followed by the data. */\n    public void writeChunk(DataOutput output, IORunner<Writes> runner) throws IOException{\n        boolean wasNested = chunkNested;\n\n        chunkNested = true;\n\n        //regions can be nested once, so use a different output if it's already nested","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/Anuken/Mindustry/blob/f695ad7e60323ebced984fa26d0bcf0bc54296b4/core/src/mindustry/io/SaveFileReader.java#L79-L115","documentation":"Thrown by SaveFileReader.readRegion after a chunk is read successfully but the declared chunk length does not match the number of bytes actually consumed from the counter (counter.count - 4, excluding the 4-byte length prefix). A length mismatch means the reader consumed a different number of bytes than the writer declared, indicating structural corruption or a serialization/version mismatch.","triggerScenarios":"readChunk returns a length L, but counter.count - 4 != L. Happens when a field is read with wrong size across versions (e.g. byte vs short vs int), a region's format changed, or the stream is misaligned by corruption.","commonSituations":"Loading a save written by a different version whose region layout shifted; a mod altered serialization in an incompatible way; bit-level corruption shifting alignment; hand-edited save files.","solutions":["Load the save in the exact game version that wrote it (region layouts are version-specific).","If you changed serialization code, ensure read/write byte counts match exactly across versions (use revisions for compatible changes).","Restore from a backup if the file is corrupt.","Catch the IOException and surface expected vs actual counts to aid diagnosis."],"exampleFix":"// before: mismatched read size across versions\n// (reading a short where an int was written shifts the counter)\n\n// after: keep read/write sizes in sync\n// write: stream.writeInt(value);\n// read:  value = stream.readInt(); // must match the write size","handlingStrategy":"try-catch","validationCode":"// pre-validate version compatibility before reading regions\nif(SaveIO.getSaveWriter(version) == null || !isCompatible(version)){\n    throw new IOException(\"Incompatible save version \" + version);\n}","typeGuard":null,"tryCatchPattern":"try{ ver.readRegion(name, stream, counter, cons); }catch(IOException e){ Log.err(\"Length mismatch in '\" + name + \"'\", e); throw e; }","preventionTips":["Keep read/write field sizes byte-identical across versions; use revisions for compatible changes.","Load saves in the exact authoring version.","Never hand-edit save bytes.","Restore from backup on corruption."],"tags":["save","io","corruption","version","serialization"],"backgroundTag":null,"analyzedSha":"f695ad7e60323ebced984fa26d0bcf0bc54296b4","analyzedAt":"2026-08-14T04:31:16.262Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}