HMCL-dev/HMCL · error · JsonParseException

Missing Project ID or File ID.

Error message

Missing Project ID or File ID.

What it means

CurseManifestFile.validate() throws JsonParseException when either projectID or fileID is 0, since valid CurseForge file entries must reference both a project and a file. These IDs are used to build download URLs via the CurseForge API.

Solutions

  1. Fill in the correct CurseForge projectID and fileID for each manifest files[] entry
  2. Re-export the modpack from the CurseForge app to regenerate valid IDs
  3. Check JSON key spelling matches the @SerializedName annotations (projectID, fileID)

Example fix

// before
{"files":[{"projectID":0,"fileID":0,"required":true}]}
// after
{"files":[{"projectID":238222,"fileID":2919425,"required":true}]}
Defensive patterns

Strategy: validation

Validate before calling

manifest.getFiles().forEach(f -> { if (f.getProjectID() == 0 || f.getFileID() == 0) throw new IllegalArgumentException("Bad CurseForge file entry: " + f); });

Type guard

boolean hasCurseIds(CurseManifestFile f) { return f.getProjectID() != 0 && f.getFileID() != 0; }

Try / catch

try { manifestFile.validate(); } catch (JsonParseException e) { log.error("Manifest file entry missing IDs", e); }

Prevention

When it happens

Trigger: Parsing a curse manifest whose files[] entry contains projectID=0 and/or fileID=0 (missing or defaulted numeric fields), then calling validate().

Common situations: Hand-crafted or incorrectly exported manifest.json, fields accidentally removed or renamed (JSON key mismatch such as projectId vs projectID), placeholders left unfilled.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/06f4d7270c5c041a. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/curse/CurseManifestFile.java:39

import com.google.gson.annotations.SerializedName;
import org.jackhuang.hmcl.util.gson.JsonSerializable;
import org.jackhuang.hmcl.util.gson.Validation;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;

/// @author huangyuhui
@JsonSerializable
public record CurseManifestFile(@SerializedName("projectID") int projectID,
                                @SerializedName("fileID") int fileID,
                                @SerializedName("fileName") String fileName,
                                @SerializedName("url") String url,
                                @SerializedName("required") boolean required) implements Validation {

    @Override
    public void validate() throws JsonParseException {
        if (projectID == 0 || fileID == 0)
            throw new JsonParseException("Missing Project ID or File ID.");
    }

    @Override
    @Nullable
    public String url() {
        if (url == null) {
            return fileName != null
                    ? String.format("https://edge.forgecdn.net/files/%d/%d/%s", fileID / 1000, fileID % 1000, fileName)
                    : null;
        } else {
            return url;
        }
    }

    public CurseManifestFile withFileName(String fileName) {
        return new CurseManifestFile(projectID, fileID, fileName, url, required);
    }

View on GitHub (pinned to 24702dc5a0)