Skip to content

Publishing and install errors

Every error message erp plugin publish can return, what it means, and what to do. The install pipeline is all-or-nothing and names the stage it failed at — see How does a plugin get installed?.

Before upload

plugin.json is missing mainClass (no longer raised, as of 2026-09-10)

A pure-JSON plugin publishes fine with "mainClass": null — the platform loads it via a built-in generic entry point, no jar required. Only set mainClass if your plugin ships its own Java extensions.

plugin.json is missing id/version

Fill them in. id is lowercase kebab-case; version is semver.

could not reach <url> / ERR_INVALID_URL

Wrong env base URL (erp env list), the server is down, or — on Git Bash — MSYS_NO_PATHCONV=1 needed.

Stage VALIDATING

Refusing to overwrite immutable artifact at "system/plugins/<id>/<version>/..."

You already stored a .spk under this <version>, the new bytes differ, and that version genuinely shipped (it reached pf4jState: STARTED on some tenant). A shipped version is write-once — bump to the next version.

As of the 2026-09-10 publish-hardening pass this is no longer raised for a version whose prior install failed or never started (a role clash caught late, or a metadata-only plugin that isn't PF4J-loaded): re-publishing the same version now replaces the stored .spk and retries. So the "edit a page, re-publish the same version" loop works for JSON-only plugins without a bump. You still bump for every change to a version that's live and STARTED.

one or more page definitions failed block-property validation

A page sets a property that the block type doesn't have, or uses a block type that doesn't exist. Run erp plugin validate spk-assembly locally — it reports which page and which property. Ground the block first with erp blocks list --type <name>.

schema validation failure on an artifact

The named file doesn't match its schema. Run erp schema validate <file> --schema <name> and node developer-docs/examples/test-examples.mjs.

Stage LOADING

There is an already loaded plugin ... with the same id (...)

A previous version of this plugin id is still loaded in PF4J. The platform cannot hold two versions of one id at once.

  • Normal upgrade / in-place re-publish: handled — the publish path now stops + unloads the currently-loaded same-id plugin (with a GC hint) before loading the new bundle. erp plugin publish / spark publish also auto-retry once with a force-unload if they still see this error.

  • Wedged load (state: uninstalled in erp plugin list but pf4jState: STARTED): a crash or a transient Failed to obtain JDBC Connection during uninstall aborted the unload. Recover without a backend restart:

    bash
    erp plugin force-unload <plugin-id>
    erp plugin publish <plugin>-<next>.spk --tenant 2

    force-unload best-effort stops, unloads and GC-hints the stuck classloader, then reports pf4jState (NOT_LOADED on success). It's idempotent and does not need an active install row. erp plugin publish --force runs it for you.

plugin manager rejected the package (missing/invalid plugin.json or mainClass?)

The lib/*.jar doesn't contain the class named by mainClass, or isn't a valid plugin jar. Recompile the stub; confirm jar -tf spk-assembly/lib/*.jar lists your .class file at the right package path.

After code load (rolled back)

A role named "X" already exists

Another installed plugin owns a role with that display name. Rename yours.

Role "X" (code "Y") already exists owned by "Z"

Another plugin owns that role code. Change your code. (This survives a plugin's own uninstall — a role created by example-plugin blocks office-equipment from creating one with the same code.)

seed-data failure — entity not found

A metadata/seed-data/*.json targets an entity your plugin doesn't ship. Ship the metadata/entities/*.json (or, for a shared config entity, ship the additive version of it).

Verify a successful publish

bash
erp plugin list

Your plugin: "state": "installed", "pf4jState": "STARTED", "migrationStatus": "INSTALLED". Then hit a page or entity endpoint to confirm the new content is live.

See also