Managing Modpacks

Managing Modpacks

View installed modpack status, update to new versions, change modpacks, and understand the tracking system.


Installed Modpack View

The Installed tab shows information about the currently installed modpack:

Field Description
Provider Which platform it came from (Modrinth, CurseForge, etc.)
Modpack Name Display name
Version Currently installed version
Installed At When it was installed (ISO timestamp)

This data comes from .installed_modpack.json on the server root.


Updating a Modpack

How Update Detection Works

When viewing a modpack you already have installed:

  1. Plugin reads .installed_modpack.json for current version
  2. Compares with available versions from the provider
  3. Newer versions shown with Update option

Update Options

When updating (🟠 type), you get two choices:

Option What Happens
Update (Delete configs) Old configs deleted, new modpack configs used
Update (Keep configs) Old configs preserved and merged with new ones

Recommended: "Keep configs" for most updates — preserves your customizations.

After Updating

  1. Wait for the 8-step progress to complete
  2. Restart the server
  3. Verify the modpack loads correctly
  4. Check merged configs for any issues

Changing to a Different Modpack

Changing modpacks (🔴 type) is the most destructive operation:

  1. Full backup created automatically
  2. All files deleted (except worlds, plugins, logs)
  3. Cross-plugin cleanup runs (Game Mods + Minecraft Plugins data cleared)
  4. New modpack downloaded and installed
  5. Important files restored

Warning: Changing modpacks clears all mod and plugin tracking data from other Eranio plugins.


Reinstalling the Current Modpack

Reinstalling (🔵 type) re-downloads and re-installs the same modpack and version:

  • Useful for fixing corrupted installations
  • Backup created before reinstall
  • Configs can be kept or replaced

Console Widget

A widget on the server console page tracks modpack status:

  • Cache: 5 minutes for update checks
  • Shows installed modpack info
  • Alerts when updates are available with redirect to Modpack Browser

Troubleshooting

".installed_modpack.json" is missing

  • No modpack has been installed via the plugin yet
  • File was manually deleted
  • The plugin treats this as a Fresh install

Tracking file is invalid

  • One or more of the 6 required fields is missing
  • File contains invalid JSON
  • Treated as Fresh install

Modpack loads but crashes on start

  1. Check server console for error messages
  2. Verify Java version matches modpack requirements (8, 11, 16, 17, or 21)
  3. Check RAM allocation — modpacks often need 4-8+ GB
  4. Verify mod loader version compatibility
  5. Try reinstalling with "Delete configs" option

Config merge caused issues

  • Some mods change config format between versions — merge can conflict
  • Solution: Reinstall with "Delete configs", then manually re-apply your changes
  • Check storage/logs/laravel.log for merge warnings

Server stuck on installer egg

  • Installation may have failed during Step 4-5
  • Manually switch the egg back to runtime in Admin Panel → Servers → [Server] → Build
  • Check logs for timeout or download errors

Cross-plugin data not cleared

  • Other plugins (Game Mods, Minecraft Plugins) may not be installed
  • Log shows "GameMods classes not found" or "MinecraftPlugins classes not found"
  • Non-critical — can manually clear via each plugin's "Clear Cache" button

⚠️ "Clear Cache" clears everything

  • The modpack plugin's clearCache() uses Cache::flush() which is a global cache flush
  • This clears ALL cached data for ALL plugins and the entire panel
  • After clearing: other plugins may be slow until their caches repopulate
  • Use sparingly — prefer waiting for TTL expiry when possible

Provider Internal Functions Reference

This section documents the internal PHP code for each provider integration, including API parameters, response schemas, rate limits, and response processing logic.

CurseForge Provider (Modpacks) — Complete Reference

Uses the same CurseForge API as the other plugins but with classId=4471 (Modpacks).

API Endpoint Parameters — GET /v1/mods/search

Parameter Type Required Default Description
gameId int Yes 432 Always Minecraft
classId int Yes 4471 Modpacks (NOT 9137 for mods, NOT 5 for plugins!)
searchFilter string No Full-text search
sortField int No 2 1=Featured (with query), 2=Popularity (without)
sortOrder string No desc Sort direction
gameVersion string No MC version filter (e.g. 1.20.1)
modLoaderType int No 1=Forge, 4=Fabric, 5=Quilt, 6=NeoForge
pageSize int No 20 Max 50
index int No 0 Pagination offset

Response Schema — Search Results

Field Type Description
data[].id int CurseForge modpack ID
data[].name string Modpack display name
data[].slug string URL slug
data[].summary string Short description
data[].authors[].name string Author name
data[].downloadCount int Total downloads
data[].logo.url string Modpack logo URL
data[].latestFiles[] array File objects with download info
data[].latestFiles[].serverPackFileId int|null Server-specific pack file ID
data[].latestFiles[].modules[].name string e.g. "overrides", "manifest.json"
data[].latestFiles[].modules[].fingerprint int Murmur2 hash for verification
pagination.totalCount int Total results

Rate Limits

Tier Limit Notes
Free API key ~1000 req/hour Shared across all CurseForge plugins
When exceeded HTTP 429 Wait 60s, response includes Retry-After
// Modpack search (classId=4471) — the only difference from mods/plugins
$response = Http::withHeaders(['x-api-key' => $apiKey])
    ->timeout($timeout)
    ->get('https://api.curseforge.com/v1/mods/search', [
        'gameId'        => 432,      // Minecraft
        'classId'       => 4471,     // ⚠ Modpacks — NOT 9137 (Mods) or 5 (Plugins)!
        'searchFilter'  => $query,
        'sortField'     => $query ? 1 : 2,
        'sortOrder'     => 'desc',
        'gameVersion'   => $mcVersion,
        'pageSize'      => $perPage,
        'index'         => ($page - 1) * $perPage,
    ]);
// Response processing — modpacks to card data
$results = $response->json('data', []);
$packs = collect($results)->map(function ($mod) {
    return [
        'id'         => $mod['id'],
        'name'       => $mod['name'],
        'slug'       => $mod['slug'],
        'summary'    => $mod['summary'],
        'author'     => $mod['authors'][0]['name'] ?? 'Unknown',
        'downloads'  => $mod['downloadCount'],
        'icon_url'   => $mod['logo']['url'] ?? null,
        'provider'   => 'curseforge',
    ];
});
// Server pack detection — prefer server pack over client pack
// Some CurseForge modpacks offer a separate "server pack" file
if (!empty($fileData['serverPackFileId'])) {
    $serverPack = $this->getFile($modId, $fileData['serverPackFileId']);
    // Use server pack instead for server installations — smaller, no client-side mods
}
// Modpack file structure — overrides/ folder + manifest.json
// CurseForge modpacks contain Murmur2-fingerprinted modules
foreach ($file['modules'] as $module) {
    // module['name']        => e.g. "overrides", "manifest.json"
    // module['fingerprint'] => Murmur2 hash for file integrity verification
    // overrides/ folder is extracted to the server root
}

CurseForge Modpacks Provider Error Map

Error / Log Message Cause Impact Solution
classId=9137 or classId=5 results Wrong classId used Mods/plugins shown instead of modpacks Ensure classId=4471 for modpacks
downloadUrl null Author restricted download Manual download needed Download from CurseForge website
serverPackFileId null No server pack uploaded Client pack used — may contain client mods Extract server files manually or wait for author to upload
fingerprint mismatch Modified or corrupted files Integrity check fails Re-download from CurseForge
CurseForge API key not set No key configured All CurseForge searches fail Set CURSEFORGE_API_KEY in settings

Modrinth Provider (Modpacks) — Complete Reference

API Endpoint Parameters — GET /v2/search

Parameter Type Required Default Description
query string No Search terms
facets string (JSON) Yes see below Must include project_type:modpack
limit int No 20 Max 100
offset int No 0 Pagination offset

Modpack-Specific Facets

// The plugin builds facets specifically for modpacks
$facets = [
    ['project_type:modpack'],                    // Must be modpack, not mod/plugin
];
if ($mcVersion) $facets[] = ["versions:$mcVersion"];
if ($loader)    $facets[] = ["categories:$loader"];  // e.g., categories:forge

$params['facets'] = json_encode($facets);

Response Schema — Search Results

Field Type Description
hits[].project_id string Modrinth project ID
hits[].title string Modpack name
hits[].slug string URL slug
hits[].description string Short description
hits[].author string Author username
hits[].downloads int Total downloads
hits[].icon_url string Modpack icon URL
hits[].versions string[] Supported MC versions
hits[].categories string[] Includes loader info (forge, fabric, etc.)
total_hits int Total results for pagination

Rate Limits

Tier Limit Notes
Unauthenticated 300 req/min per IP X-Ratelimit-Remaining header available
When exceeded HTTP 429 Retry-After header provided
// Modpack search with modpack-specific facets
$response = Http::timeout($timeout)
    ->get('https://api.modrinth.com/v2/search', [
        'query'  => $query,
        'facets' => json_encode([
            ['project_type:modpack'],
            $mcVersion ? ["versions:$mcVersion"] : [],
            $loader ? ["categories:$loader"] : [],
        ]),
        'limit'  => $perPage,
        'offset' => ($page - 1) * $perPage,
    ]);

// Response processing
$hits = $response->json('hits', []);
$packs = collect($hits)->map(function ($hit) {
    return [
        'id'         => $hit['project_id'],
        'name'       => $hit['title'],
        'slug'       => $hit['slug'],
        'summary'    => $hit['description'],
        'author'     => $hit['author'],
        'downloads'  => $hit['downloads'],
        'icon_url'   => $hit['icon_url'] ?? null,
        'versions'   => $hit['versions'] ?? [],
        'provider'   => 'modrinth',
    ];
});
// Modrinth modpacks use .mrpack format (ZIP file)
// Structure: modrinth.index.json + overrides/
$manifest = json_decode($zip->getFromName('modrinth.index.json'), true);
foreach ($manifest['files'] as $file) {
    // $file['path']            => target path relative to server root
    // $file['downloads']       => array of download mirror URLs
    // $file['hashes']['sha1']  => SHA-1 for integrity check
    // $file['hashes']['sha512'] => SHA-512 for integrity check
    // $file['fileSize']        => expected file size in bytes
}
// overrides/ directory is extracted to server root

Modrinth Modpacks Provider Error Map

Error / Log Message Cause Impact Solution
No results with project_type:modpack Modpack not tagged on Modrinth Missing modpacks Try other providers (CurseForge, FTB)
SHA-1/SHA-512 mismatch Download corrupted File integrity fails Clear cache, re-download
modrinth.index.json missing Corrupted .mrpack file Install fails Re-download from Modrinth
Wrong project_type Facets misconfigured Mods mixed with modpacks Ensure project_type:modpack facet

FeedTheBeast (FTB) Provider — Complete Reference

FTB API at https://api.feed-the-beast.com/v1. No authentication required. Uses a unique two-step loading pattern.

API Endpoint Parameters — GET /v1/modpacks/public/modpack/all

Parameter Type Required Default Description
(none) Returns all public pack IDs

API Endpoint Parameters — GET /v1/modpacks/public/modpack/{packId}

Parameter Type Required Default Description
packId int (path) Yes FTB pack ID

API Endpoint Parameters — GET /v1/modpacks/public/modpack/search/{limit}

Parameter Type Required Default Description
limit int (path) Yes Max results (e.g. 100)
term string (query) Yes Search term

Response Schema — All Packs

Field Type Description
packs int[] Array of pack IDs
total int Total number of public packs

Response Schema — Pack Details

Field Type Description
id int Pack ID
name string Pack name
synopsis string Short description
description string Full HTML description
art[].url string Logo/splash image URL
art[].type string "square" or "splash"
versions[].id int Version ID (for download)
versions[].name string Version string (e.g. 1.2.3)
versions[].type string "Release", "Beta", "Alpha"
tags[].name string Tag labels
installs int Total installs
plays int Total play sessions
status string "released" or "private"

Response Schema — Search

Field Type Description
packs int[] Matching pack IDs
curseforge int[] CurseForge cross-reference IDs
total int Total matches

Rate Limits

Tier Limit Notes
Public No documented limit Fair use — avoid rapid bulk requests
De facto ~100 detail requests/min Each pack detail is a separate HTTP call
// Step 1: Get all FTB pack IDs
$all = Http::timeout($timeout)
    ->get('https://api.feed-the-beast.com/v1/modpacks/public/modpack/all');
// Returns: { "packs": [100, 200, 300, ...], "total": 150 }
// ⚠ Pack ID 81 is FTB's test pack — MUST be filtered out!
$packIds = collect($all->json('packs', []))->reject(fn ($id) => $id === 81);
// Step 2: Fetch details for each pack (batched with concurrency control)
$packs = $packIds->map(function ($id) use ($timeout) {
    $pack = Http::timeout($timeout)
        ->get("https://api.feed-the-beast.com/v1/modpacks/public/modpack/{$id}")
        ->json();

    return [
        'id'         => $pack['id'],
        'name'       => $pack['name'],
        'summary'    => $pack['synopsis'] ?? '',
        'icon_url'   => collect($pack['art'] ?? [])->firstWhere('type', 'square')['url'] ?? null,
        'downloads'  => $pack['installs'] ?? 0,
        'versions'   => collect($pack['versions'] ?? [])->pluck('name')->toArray(),
        'provider'   => 'ftb',
    ];
});
// Search FTB packs — returns pack IDs that match, then fetch details
$search = Http::timeout($timeout)
    ->get("https://api.feed-the-beast.com/v1/modpacks/public/modpack/search/100", [
        'term' => $query,
    ]);
// Returns: { "packs": [100, 200], "curseforge": [], "total": 2 }
// The numeric suffix (100) is the max result count
// "curseforge" array contains cross-referenced CurseForge mod IDs
// Server pack download — requires version ID for platform-specific download
$serverUrl = "https://api.feed-the-beast.com/v1/modpacks/public/modpack/{$packId}/versions/{$versionId}/server/linux";
// Alternative: .../server/windows for Windows servers
// Downloads a server installer script/ZIP

FTB-specific behaviours:

  • Two-step loading: First GET .../all for IDs → then GET .../{id} per pack for details
  • Search returns pack IDs (not full objects) — requires second step to get details
  • Pack ID 81 is always filtered out — it's FTB's internal test pack
  • art array has type: "square" (logo) and type: "splash" (banner)
  • status: "released" = public; "private" = needs special access code
  • Server downloads are platform-specific: /server/linux or /server/windows
  • Version type can be "Release", "Beta", or "Alpha" — only Release shown by default
  • Modpack data is cached for 24 hours per pack

FTB Provider Error Map

Error / Log Message Cause Impact Solution
Pack ID 81 appearing Test pack not filtered Broken pack in list Plugin filters it — if visible, clear cache
Empty version list Pack is private/restricted Cannot install Check pack status on FTB website
Server download 404 No server distribution rights Server pack unavailable Contact pack author for server pack
Slow pack loading Two-step fetch (100+ individual requests) Page loads slowly Normal for FTB — wait for cache to populate
Stale data Pack list cached 24h Outdated info Clear cache for immediate refresh
status: "private" Pack not publicly accessible Cannot list/install Need access code from pack author

Technic Platform Provider — Complete Reference

Technic API at https://api.technicpack.net. Requires a build parameter for launcher version compatibility.

API Endpoint Parameters — GET /search/modpacks/{query}

Parameter Type Required Default Description
query string (path) Yes Search term (URL-encoded)
build int Yes 822 Technic Launcher build number — must be valid

API Endpoint Parameters — GET /modpack/{slug}

Parameter Type Required Default Description
slug string (path) Yes Modpack slug (e.g. tekkit)
build int Yes 822 Technic Launcher build number

Response Schema — Search (Dictionary Format!)

Field Type Description
modpacks object Dictionary keyed by slug, NOT an array!
modpacks.{slug}.name string Modpack internal name
modpacks.{slug}.displayName string Human-readable name
modpacks.{slug}.url string Technic website URL
modpacks.{slug}.platformUrl string Platform page link
modpacks.{slug}.minecraft string Minecraft version
modpacks.{slug}.runs int Total launches
modpacks.{slug}.ratings int User ratings count

Response Schema — Pack Details

Field Type Description
name string Internal pack name
displayName string Human-readable display name
url string Direct download URL (ZIP)
platformUrl string Technic platform page URL
minecraft string Minecraft version string
runs int Total launch count
ratings int Rating count
icon.url string Pack icon URL
logo.url string Pack logo URL
background.url string Background image URL

Rate Limits

Tier Limit Notes
Public No documented limit Fair use expected
De facto ~200 req/min Aggressive requests may be throttled
// Search Technic modpacks — note dictionary response format
$response = Http::timeout($timeout)
    ->get("https://api.technicpack.net/search/modpacks/{$query}", [
        'build' => 822,  // ⚠ Must be a valid Technic Launcher build number!
    ]);

// IMPORTANT: Technic returns a DICTIONARY keyed by slug, not an array!
$modpacks = $response->json('modpacks', []);
// $modpacks = { "tekkit": { ... }, "hexxit": { ... } }
// Must use array_values() or collect() to iterate
$packs = collect($modpacks)->map(function ($pack, $slug) {
    return [
        'id'         => $slug,
        'name'       => $pack['displayName'] ?? $pack['name'],
        'summary'    => '', // Technic search doesn't return descriptions
        'downloads'  => $pack['runs'] ?? 0,
        'mc_version' => $pack['minecraft'] ?? 'Unknown',
        'provider'   => 'technic',
    ];
});
// Get specific modpack details — includes download URL
$pack = Http::timeout($timeout)
    ->get("https://api.technicpack.net/modpack/{$slug}", [
        'build' => 822,
    ])->json();

// Response processing — extract pack metadata
$result = [
    'name'        => $pack['displayName'] ?? $pack['name'],
    'url'         => $pack['url'],              // Direct ZIP download URL
    'platform'    => $pack['platformUrl'],       // Technic website page
    'mc_version'  => $pack['minecraft'] ?? '',   // MC version string
    'runs'        => $pack['runs'] ?? 0,
    'icon'        => $pack['icon']['url'] ?? null,
    'logo'        => $pack['logo']['url'] ?? null,
];
// url field → direct download of modpack ZIP (server or client)

Technic-specific behaviours:

  • Search returns a dictionary keyed by slug, not an array — must be converted
  • build parameter is required — controls API compatibility version
  • minecraft field contains the MC version string directly (not an array)
  • platformUrl links to the Technic website page for the pack
  • url field → direct ZIP download (used for both client and server packs)
  • runs counts the number of times the modpack has been launched (not downloads)
  • No server-specific packs — same ZIP for client and server; manual extraction needed
  • No pagination — search returns all matching packs at once

Technic Provider Error Map

Error / Log Message Cause Impact Solution
No results Build number outdated or query too specific Empty search Update build parameter, broaden query
Modpack not downloading URL points to client pack or is invalid Download fails Verify URL is accessible, check Technic website
"0 runs" shown Pack statistics unavailable Cosmetic Normal for very old or inactive packs
Dictionary parse error Code expects array but gets object Display issues Plugin handles this — clear cache if issues persist
build parameter rejected Technic changed required build version API returns error Check Technic API for current valid build number

ATLauncher Provider — Complete Reference

ATLauncher uses a GraphQL API at https://api.atlauncher.com/v2/graphql. This is the only GraphQL-based provider.

GraphQL Endpoint — POST /v2/graphql

Parameter Type Required Description
query string Yes GraphQL query string

Available Query Fields

Field Path Type Description
packs(first: N) query Root query with pagination
edges[].node.id int Pack unique ID
edges[].node.name string Pack display name
edges[].node.description string Full pack description
edges[].node.type string "public", "semi-public", "private"
edges[].node.versions[].version string Pack version string
edges[].node.versions[].minecraft string MC version for this pack version

Rate Limits

Tier Limit Notes
Public No documented limit GraphQL overhead is higher per request
De facto ~50 req/min Complex queries may be slower
// GraphQL query for ATLauncher pack listing
$query = <<<'GRAPHQL'
{
    packs(first: 20) {
        edges {
            node {
                id
                name
                description
                type
                versions {
                    version
                    minecraft
                }
            }
        }
    }
}
GRAPHQL;

$response = Http::timeout($timeout)
    ->post('https://api.atlauncher.com/v2/graphql', ['query' => $query]);
// Response processing for ATLauncher GraphQL
$data = $response->json('data.packs.edges', []);
$packs = collect($data)->map(function ($edge) {
    $node = $edge['node'];
    $latestVersion = collect($node['versions'] ?? [])->first();
    return [
        'id'         => $node['id'],
        'name'       => $node['name'],
        'summary'    => Str::limit($node['description'] ?? '', 200),
        'mc_version' => $latestVersion['minecraft'] ?? 'Unknown',
        'pack_version' => $latestVersion['version'] ?? '',
        'type'       => $node['type'],  // "public", "semi-public", "private"
        'provider'   => 'atlauncher',
    ];
})->filter(fn ($p) => $p['type'] === 'public');  // Only show public packs
// Server download URL pattern — requires exact pack name and version
$serverUrl = "https://servers.atlauncher.com/server/{$packName}/{$version}";
// Not all packs have server distribution rights — 404 if unavailable

ATLauncher-specific behaviours:

  • GraphQL only — no REST API endpoints available
  • Pack types: "public" (open), "semi-public" (needs code), "private" (restricted)
  • Only "public" packs shown in default listing — filtered client-side
  • Server downloads at servers.atlauncher.com — not all packs have server distribution
  • GraphQL queries have higher overhead than REST — slower response times expected
  • Version list includes MC version mapping per pack version
  • No search endpoint — filtering is done client-side on the returned list
  • description may contain HTML — stripped to plain text for display

ATLauncher Provider Error Map

Error / Log Message Cause Impact Solution
GraphQL error / empty response Malformed query or API issue No packs returned Clear cache, verify API is responding
"Private pack" or type: "semi-public" Pack requires access code Cannot install Contact pack author for access code
Server download 404 No server distribution rights for this pack Can't auto-install server Use client pack, extract server files manually
Slow response (~2-5s) GraphQL parsing overhead Longer page load Normal for ATLauncher — increase timeout if needed
Empty versions array Pack has no published versions Cannot install Check ATLauncher website for pack status

VoidsWrath Provider — Complete Reference

VoidsWrath uses a static JSON file — no dynamic API, no search, no pagination.

API Endpoint — GET /minecraft-modpack-server-installer/voidswrath.json

Parameter Type Required Description
(none) Returns entire catalogue as JSON array

Response Schema — Pack Array

Field Type Description
[].name string Modpack name
[].version string Current version string
[].mcVersion string Minecraft version
[].url string Direct ZIP download URL
[].serverUrl string|null Server-specific download URL
[].description string Pack description
[].icon string Icon image URL

Rate Limits

Tier Limit Notes
Public No limit Static file hosted on ric-rac.org
De facto N/A File is small (~50KB), fast to fetch
// Fetch entire VoidsWrath catalogue — one request, all packs
$response = Http::timeout($timeout)
    ->get('https://www.ric-rac.org/minecraft-modpack-server-installer/voidswrath.json');

$allPacks = $response->json();
// Returns: Full array of all packs — typically <20 entries

// Response processing — no filtering needed
$packs = collect($allPacks)->map(function ($pack) {
    return [
        'id'         => Str::slug($pack['name']),         // Generated slug
        'name'       => $pack['name'],
        'summary'    => $pack['description'] ?? '',
        'mc_version' => $pack['mcVersion'] ?? 'Unknown',
        'version'    => $pack['version'] ?? '',
        'icon_url'   => $pack['icon'] ?? null,
        'download'   => $pack['url'],                      // Direct ZIP URL
        'server_url' => $pack['serverUrl'] ?? null,        // Server-specific URL
        'provider'   => 'voidswrath',
    ];
});

VoidsWrath-specific behaviours:

  • No search — all packs loaded at once from a single static JSON file
  • No pagination — entire catalogue in one request (typically <20 packs)
  • No API versioning — if the JSON structure changes, fetching may break
  • Downloads point directly to ZIP files — no CDN or mirror system
  • Pack updates require full re-download — no differential/incremental updates
  • serverUrl may be null for packs without server distribution
  • Static file hosted on ric-rac.org — availability depends on this single host
  • Very small catalogue compared to other providers
  • Cache TTL should be generous (24h+) since data changes rarely

VoidsWrath Provider Error Map

Error / Log Message Cause Impact Solution
Static JSON unreachable Host ric-rac.org down or DNS failure All VoidsWrath packs unavailable Wait for host to recover, check DNS
Old pack versions shown JSON file not updated by VoidsWrath team Outdated info Contact VoidsWrath or check their website
All packs shown at once Expected — no pagination or filtering Not an error Normal behaviour for VoidsWrath
Download fails / 404 Direct ZIP link broken or file moved Install fails Report to VoidsWrath, check for updated URL
JSON parse error Server returned HTML error page instead of JSON No packs shown Host may be misconfigured — retry later

Cross-Provider Patterns (Modpacks)

// Modpack installation flow (all providers)
// 1. Download pack archive (ZIP/mrpack)
// 2. Switch to installer egg if needed
// 3. Extract to server directory
// 4. Merge configs (if updating)
// 5. Switch back to game egg
// 6. Clear Game Mods + Plugins data (cross-plugin cleanup)
// Backup before install — creates timestamped archive
$backupName = "backup_" . date('Y-m-d_H-i-s') . ".tar.gz";
// If backup fails, installation proceeds with warning notification
// Search retry logic — same as other plugins
$results = $this->search($query, $mcVersion, $loader);
if (empty($results) && $mcVersion) {
    $results = $this->search($query, null, $loader);
    // Retry without version filter — version may not be indexed yet
}

Complete Notification Reference

Event Type Title Body
Modpack installed Success Installed "Modpack [name] installed successfully"
Install failed Danger Failed "Modpack installation failed" + error
Update started Info Updating "Updating modpack to version X"
Update success Success Updated "Modpack updated to version X"
Update failed Danger Failed "Update failed" + error
Backup created Info Backup "Backup created before installation"
Backup failed Warning Backup "Backup failed — proceeding without backup"
Egg switch started Info Egg "Switching to installer egg"
Egg switch failed Danger Egg "Failed to switch egg"
Cache cleared Success Cache "Cache cleared"
Version detected Success Detection "Minecraft version detected: X.X.X"
Config merge warning Warning Config "Config format changed — manual review recommended"
Cross-plugin cleanup Info Cleanup "Clearing Game Mods and Minecraft Plugins data"
CurseForge key missing Warning Config "CurseForge API key not set"

HTTP Error Code Reference (All Providers)

HTTP Status Source Meaning Action
200 Any Success
400 CurseForge Bad request (invalid params) Check gameId, classId
401 CurseForge API key invalid Replace at console.curseforge.com
403 CurseForge Key lacks permissions Regenerate key
403 Plugin Feature flag modpack-installer missing Add to Egg Features
404 Any Pack/version not found Pack may have been removed
404 ATLauncher Server pack not available Not all packs have server distribution
429 CurseForge Rate limit Wait 60s, increase cache
429 Modrinth Rate limit Wait for Retry-After, reduce frequency
500 Any provider Server error Retry later
502/503 Any Gateway/maintenance Provider offline
0 / timeout Any Connection timeout Increase timeout, check firewall
0 / timeout FTB Pack detail fetch timeout Two-step loading is slow — increase timeout

Provider-Specific Error Table

CurseForge (Modpacks) Errors

Error / Symptom Cause Solution
"CurseForge API key not set" No key configured Enter key in settings
HTTP 403 Key invalid Re-generate at console.curseforge.com
Empty download URL Author restriction Download manually from website
Server pack not available Author didn't upload server pack Use client pack with manual extraction
Wrong classId results classId mismatch Ensure classId=4471 used
Fingerprint mismatch Files modified/corrupted Re-download from CurseForge

Modrinth (Modpacks) Errors

Error / Symptom Cause Solution
No results Modpack not tagged correctly Try CurseForge or FTB
.mrpack extraction fails Corrupt download or ZIP Re-download, check disk space
Hash mismatch File corrupted in transit Clear cache, re-download
Missing overrides .mrpack structure changed Update plugin to latest version

FTB Errors

Error / Symptom Cause Solution
Pack ID 81 appearing Test pack in API Plugin filters it — if visible, clear cache
Empty version list Pack is private/restricted Check pack status on FTB website
Server download 404 No server distribution Contact pack author
Slow pack loading Two-step fetch (IDs → details) Normal — each pack needs individual fetch
Stale data Pack list cached 24h Clear cache for immediate refresh
status: "private" Pack not public Cannot install without access

Technic Errors

Error / Symptom Cause Solution
No results Build number outdated Update build parameter (currently 822)
Modpack not downloading URL points to client pack Verify server pack availability
"0 runs" shown Pack statistics not available Normal for older/inactive packs
Dictionary vs array confusion Technic returns dict by slug Plugin handles this — clear cache if display issues
build parameter rejected Technic changed valid build range Check Technic documentation for current build values

ATLauncher Errors

Error / Symptom Cause Solution
GraphQL error Malformed query Clear cache, retry
"Private pack" Pack requires special access Contact pack author for access code
Server download 404 No server distribution rights Use client pack for manual extraction
Slow response GraphQL parsing overhead Normal for ATLauncher — increase timeout
Empty results No public packs matching filter Check ATLauncher website

VoidsWrath Errors

Error / Symptom Cause Solution
Static JSON unreachable Host ric-rac.org down Wait for host to recover
Old pack versions JSON file not updated Contact VoidsWrath team
All packs shown at once Expected — no pagination Not an error
Download fails Direct ZIP link broken Report to VoidsWrath
JSON parse error Server returned HTML instead of JSON Host misconfigured — retry later