{"record":{"id":"63ac76d14951abf6","repo":"Anuken/Mindustry","slug":"failed-to-read-tile-entity-of-block","errorCode":null,"errorMessage":"Failed to read tile entity of block: {}","messagePattern":"Failed to read tile entity of block: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"core/src/mindustry/io/SaveVersion.java","lineNumber":369,"sourceCode":"                //must be assigned after setBlock, because that can reset data\n                if(hadData){\n                    tile.data = data;\n                    tile.floorData = floorData;\n                    tile.overlayData = overlayData;\n                    tile.extraData = extraData;\n                    context.onReadTileData();\n                }\n\n                if(hadEntity){\n                    if(isCenter){ //only read entity for center blocks\n                        if(block.hasBuilding()){\n                            try{\n                                readChunkReads(stream, (in, len) -> {\n                                    byte revision = in.b();\n                                    tile.build.readAll(in, revision);\n                                });\n                            }catch(Throwable e){\n                                throw new IOException(\"Failed to read tile entity of block: \" + block, e);\n                            }\n                        }else{\n                            //skip the entity region, as the entity and its IO code are now gone\n                            skipChunk(stream);\n                        }\n\n                        context.onReadBuilding();\n                    }\n                }else if(!hadData){ //never read consecutive blocks if there's data\n                    int consecutives = stream.readUnsignedByte();\n\n                    for(int j = i + 1; j < i + 1 + consecutives; j++){\n                        context.tile(j).setBlock(block);\n                    }\n\n                    i += consecutives;\n                }\n            }","sourceCodeStart":351,"sourceCodeEnd":387,"githubUrl":"https://github.com/Anuken/Mindustry/blob/f695ad7e60323ebced984fa26d0bcf0bc54296b4/core/src/mindustry/io/SaveVersion.java#L351-L387","documentation":"In SaveVersion.readMap, when a center tile block has a building, the per-tile entity region is read via readChunkReads then tile.build.readAll(in, revision). Any Throwable thrown during that read is caught and rethrown as an IOException with the block name attached, which then surfaces through readRegion as an 'Error reading region map' wrapper. The original cause is preserved as the exception cause.","triggerScenarios":"Block class schema changed between save and load (added/removed serialized fields); modded block whose class is missing; corrupt entity byte stream causing ArrayIndexOutOfBounds / negative array size in readAll; revision byte does not match a known read revision of the block.","commonSituations":"Loading a save after updating the game and a block's serialization changed without a migration path; loading a modded save without the mod enabled; partial save corruption in the entities region; a custom block whose readAll implementation throws.","solutions":["Inspect the wrapped cause (getCause) and the block name in the message: a NoSuchField/ClassCastException indicates a version drift, an EOFException indicates truncation.","Re-save the world in a build whose block IO matches, then load.","Ensure the same mods (and versions) that created the save are enabled when loading.","If you author a custom block, implement readAll defensively across revisions and bump the revision byte only when adding fields.","Use the -backup file via SaveIO.load(Fi) which already retries once."],"exampleFix":"// before\nSaveIO.load(file); // IOException(\"Failed to read tile entity of block: <name>\")\n\n// after\ntry {\n    SaveIO.load(file);\n} catch (SaveException e) {\n    Throwable c = e.getCause(); // may chain through \"Error reading region map\"\n    while (c != null && c.getCause() != null) c = c.getCause();\n    Log.err(\"Tile entity read failed for save @\", file, c);\n}","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n    SaveIO.load(file);\n} catch (SaveException e) {\n    Throwable c = e.getCause();\n    while (c != null && c.getCause() != null) c = c.getCause();\n    if (c != null && c.getMessage() != null && c.getMessage().startsWith(\"Failed to read tile entity\")) {\n        // block IO mismatch; report block name\n    }\n}","preventionTips":["Keep the same game build (and mods) when loading a save that created it.","Implement readAll defensively across revisions for custom blocks.","Use the backup file when the primary entities region is corrupt."],"tags":["save-io","deserialization","block-io","mod-compat","data-corruption"],"backgroundTag":null,"analyzedSha":"f695ad7e60323ebced984fa26d0bcf0bc54296b4","analyzedAt":"2026-08-14T04:31:16.262Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}