{"record":{"id":"441c4f42cb67560f","repo":"Anuken/Mindustry","slug":"incorrect-header-expecting-actual","errorCode":null,"errorMessage":"Incorrect header! Expecting: {}; Actual: {}","messagePattern":"Incorrect header! Expecting: (.+?); Actual: (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"core/src/mindustry/io/SaveIO.java","lineNumber":190,"sourceCode":"            ver.read(stream, counter, new SaveReadState(context));\n            Events.fire(new SaveLoadEvent(context.isMap()));\n        }catch(Throwable e){\n            throw new SaveException(e);\n        }finally{\n            world.setGenerating(false);\n            content.setTemporaryMapper(null);\n        }\n    }\n\n    public static SaveVersion getVersion(){\n        return versionArray.peek();\n    }\n\n    public static void readHeader(DataInput input) throws IOException{\n        byte[] bytes = new byte[header.length];\n        input.readFully(bytes);\n        if(!Arrays.equals(bytes, header)){\n            throw new IOException(\"Incorrect header! Expecting: \" + Arrays.toString(header) + \"; Actual: \" + Arrays.toString(bytes));\n        }\n    }\n\n    public static class SaveException extends RuntimeException{\n        public SaveException(Throwable throwable){\n            super(throwable);\n        }\n    }\n}\n","sourceCodeStart":172,"sourceCodeEnd":200,"githubUrl":"https://github.com/Anuken/Mindustry/blob/f695ad7e60323ebced984fa26d0bcf0bc54296b4/core/src/mindustry/io/SaveIO.java#L172-L200","documentation":"SaveIO.readHeader reads exactly header.length (4) bytes and compares them to {'M','S','A','V'}. Any mismatch throws IOException. This is the first validation performed on every save stream (before version read), so a wrong header means the file is not a Mindustry save at all or its beginning is corrupt.","triggerScenarios":"readHeader is called by both getMeta and load before anything else; feeding a non-.msav file, a text file, or a truncated/overwritten file whose first 4 bytes are not the MSAV magic. The reported Actual array shows what bytes were read.","commonSituations":"User selected the wrong file (e.g. a .png or .zip); the save was partially overwritten by another process; an OS copy truncated the file; feeding a raw deflated stream without the header.","solutions":["Confirm the file is a real .msav produced by SaveIO.write (it always writes the MSAV header first).","Inspect the Actual bytes in the message: random bytes indicate corruption; ASCII text indicates a wrong file.","Use SaveIO.isSaveValid to gate the UI; it catches this IOException and returns false.","Restore from the -backup file generated by SaveIO.save before each write."],"exampleFix":"// before\ntry (var stream = SaveIO.getStream(file)) {\n    SaveIO.readHeader(stream);\n}\n\n// after\nif (!SaveIO.isSaveValid(file)) {\n    throw new IllegalArgumentException(file + \" is not a valid Mindustry save (bad or missing MSAV header)\");\n}","handlingStrategy":"validation","validationCode":"if (!SaveIO.isSaveValid(file)) {\n    throw new IllegalArgumentException(\"Not a valid Mindustry save (bad MSAV header): \" + file);\n}","typeGuard":"null","tryCatchPattern":"try {\n    SaveIO.readHeader(stream);\n} catch (IOException e) {\n    // header mismatch; file is not a save\n}","preventionTips":["Filter file choosers to the .msav extension.","Use isSaveValid before any readHeader/getMeta call.","Restore from the -backup file when the primary header is corrupt."],"tags":["save-io","header-validation","data-corruption","file-format"],"backgroundTag":null,"analyzedSha":"f695ad7e60323ebced984fa26d0bcf0bc54296b4","analyzedAt":"2026-08-14T04:31:16.262Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}