Anuken/Mindustry · error · IOException

Incorrect header! Expecting: {}; Actual: {}

Error message

Incorrect header! Expecting: {}; Actual: {}

What it means

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.

Source

Thrown at core/src/mindustry/io/SaveIO.java:190

            ver.read(stream, counter, new SaveReadState(context));
            Events.fire(new SaveLoadEvent(context.isMap()));
        }catch(Throwable e){
            throw new SaveException(e);
        }finally{
            world.setGenerating(false);
            content.setTemporaryMapper(null);
        }
    }

    public static SaveVersion getVersion(){
        return versionArray.peek();
    }

    public static void readHeader(DataInput input) throws IOException{
        byte[] bytes = new byte[header.length];
        input.readFully(bytes);
        if(!Arrays.equals(bytes, header)){
            throw new IOException("Incorrect header! Expecting: " + Arrays.toString(header) + "; Actual: " + Arrays.toString(bytes));
        }
    }

    public static class SaveException extends RuntimeException{
        public SaveException(Throwable throwable){
            super(throwable);
        }
    }
}

View on GitHub (pinned to f695ad7e60)

Solutions

  1. Confirm the file is a real .msav produced by SaveIO.write (it always writes the MSAV header first).
  2. Inspect the Actual bytes in the message: random bytes indicate corruption; ASCII text indicates a wrong file.
  3. Use SaveIO.isSaveValid to gate the UI; it catches this IOException and returns false.
  4. Restore from the -backup file generated by SaveIO.save before each write.

Example fix

// before
try (var stream = SaveIO.getStream(file)) {
    SaveIO.readHeader(stream);
}

// after
if (!SaveIO.isSaveValid(file)) {
    throw new IllegalArgumentException(file + " is not a valid Mindustry save (bad or missing MSAV header)");
}
Defensive patterns

Strategy: validation

Validate before calling

if (!SaveIO.isSaveValid(file)) {
    throw new IllegalArgumentException("Not a valid Mindustry save (bad MSAV header): " + file);
}

Type guard

null

Try / catch

try {
    SaveIO.readHeader(stream);
} catch (IOException e) {
    // header mismatch; file is not a save
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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