Anuken/Mindustry · error · ModLoadException

Invalid file: No mod.json found.

Error message

Invalid file: No mod.json found.

What it means

loadMod resolves the mod root (zip or directory) and searches for a meta file (mod.json/plugin.hjson etc. via findMeta). If none is present it logs a warning and throws ModLoadException 'Invalid file: No mod.json found.' A mod cannot be loaded without a manifest.

Source

Thrown at core/src/mindustry/mod/Mods.java:1097

        if(OS.isMac && (!(fi instanceof ZipFi))) fi.child(".DS_Store").delete();
        Fi[] files = fi.list();
        return files.length == 1 && files[0].isDirectory() ? files[0] : fi;
    }

    /** Loads a mod file+meta, but does not add it to the list.
     * Note that directories can be loaded as mods. */
    private LoadedMod loadMod(Fi sourceFile, boolean overwrite, boolean initialize) throws Exception{

        ZipFi rootZip = null;

        try{
            Fi zip = resolveRoot(sourceFile.isDirectory() ? sourceFile : (rootZip = new ZipFi(sourceFile)));

            ModMeta meta = findMeta(zip);

            if(meta == null){
                Log.warn("Mod @ doesn't have a '[mod/plugin].[h]json' file, skipping.", zip);
                throw new ModLoadException("Invalid file: No mod.json found.");
            }

            String camelized = meta.name.replace(" ", "");
            String mainClass = meta.main == null ? camelized.toLowerCase(Locale.ROOT) + "." + camelized + "Mod" : meta.main;
            String baseName = meta.name.toLowerCase(Locale.ROOT).replace(" ", "-");

            var other = mods.find(m -> m.name.equals(baseName));

            if(other != null){
                //steam mods can't really be deleted, they need to be unsubscribed
                if(overwrite && !other.hasSteamID()){

                    //close the classloader for jar mods
                    if(!android){
                        ClassLoaderCloser.close(other.loader);
                    }else if(other.loader != null){
                        //Try to remove cache for Android 14 security problem
                        Fi cacheDir = new Fi(Core.files.getCachePath()).child("mods");

View on GitHub (pinned to f695ad7e60)

Solutions

  1. Ensure mod.json (or plugin.json/plugin.hjson/mod.hjson) exists at the root of the zip/directory.
  2. Re-zip so the manifest is at the archive's top level, not inside a subfolder.
  3. Use the exact expected manifest filename.

Example fix

// before: mymod.zip
//   mymod/
//     mod.json
// (nested -> not found)

// after: mymod.zip
//   mod.json
//   ...files...
Defensive patterns

Strategy: validation

Validate before calling

// Verify a manifest exists at the archive/directory root before importing.
Fi root = resolveRoot(source);
boolean hasMeta = false;
for(String name : new String[]{"mod.json","mod.hjson","plugin.json","plugin.hjson"}) {
    if(root.child(name).exists()){ hasMeta = true; break; }
}
if(!hasMeta) throw new ModLoadException("Invalid file: No mod.json found.");

Type guard

boolean hasManifest(Fi root){
    return java.util.List.of("mod.json","mod.hjson","plugin.json","plugin.hjson").stream().anyMatch(n -> root.child(n).exists());
}

Try / catch

try {
    mods.loadMod(file);
} catch(ModLoadException e) {
    if(e.getMessage().contains("No mod.json")) { /* fix archive layout */ }
    else throw e;
}

Prevention

When it happens

Trigger: The supplied source file/directory contains no recognized manifest at its root.

Common situations: Zipped the parent folder instead of its contents (manifest ends up nested one level deep); wrong filename; manifest named meta.json instead of mod.json; plain data archive loaded as a mod.

Related errors


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