Skip to content

Troubleshooting & FAQ

Common Issues

Platform Won't Start

Symptom: mvn spring-boot:run fails immediately.

Check:

  1. PostgreSQL is running and reachable at the configured URL
  2. METAONE_JWT_SECRET is set and at least 32 characters long
  3. The database user has CREATE TABLE permissions
bash
# Test DB connectivity
psql $SPRING_DATASOURCE_URL -c "SELECT 1"

# Verify env var is set
echo $METAONE_JWT_SECRET | wc -c   # must be > 32

mvn test Fails on Spring Context Startup

Symptom: Full test suite fails with CapabilityException class not found or CapabilityGatewayFactory initialization error.

Workaround: Run the reliable focused test instead:

bash
mvn -pl metaone-core -Dtest=ExtensionInstallerServiceTest test

Root cause: Missing CapabilityException class in metaone-sdk. Tracked in TODOS P1.


Extension Installation Fails with FAILED Status

Symptom: POST /admin/workspaces/{wsId}/extensions/install returns status: FAILED.

Steps:

  1. Check the error field in the response — it contains the failure reason
  2. Common causes:
    • Plugin JAR not found in metaone.plugins-path
    • Signature verification failed (set metaone.security.skip-signature: true in dev)
    • Workspace DB provisioning failed (check PostgreSQL permissions)
bash
# Check what's in the plugins directory
ls -la $(find . -name "plugins" -type d | head -1)

# Check installation record error
curl http://localhost:8080/api/catalog/workspaces/{wsId}/extensions/{key}

Extension Stuck in PENDING_CONFIG

Symptom: Extension is installed but status: PENDING_CONFIG and enabled: false.

Cause: The extension has required: true config fields that haven't been set.

Fix:

bash
# 1. Get the config schema and see which fields are required
curl http://localhost:8080/api/admin/workspaces/{wsId}/extensions/{key}/config

# 2. Set the required values
curl -X PUT http://localhost:8080/api/admin/workspaces/{wsId}/extensions/{key}/config \
  -H "Content-Type: application/json" \
  -d '{"fieldKey": "value", ...}'

# 3. Enable the extension
curl -X POST http://localhost:8080/api/admin/workspaces/{wsId}/extensions/{key}/enable

Capability Invocation Returns 404

Symptom: POST /workspaces/{wsId}/capabilities/{key} → 404.

Causes:

  1. Extension is not installed in this workspace
  2. Capability key is not in the manifest provides list
  3. Extension is disabled

Diagnose:

bash
# Check installed extensions
curl http://localhost:8080/api/catalog/workspaces/{wsId}/extensions

# Check registered capabilities
curl http://localhost:8080/api/catalog/workspaces/{wsId}/extensions/{key}/capabilities

In-Process Capability Handler Not Found

Symptom: Capability routes to extension but throws No handler found for key: ....

Cause: Handler annotation key doesn't match manifest key.

Check:

  • Handler: @CapabilityGroup(prefix = "crm.product") + @Capability(key = "search")crm.product.search
  • Manifest: CapabilityProvide("crm.product.search", "workspace")

Both must produce the exact same string. Keys are case-sensitive.


Events Not Being Delivered

Symptom: Published events don't reach extension handlers.

Diagnose:

  1. Verify the extension subscribes to this event key in its manifest (consumesEvents)
  2. Verify the extension is enabled
  3. For in-process: verify the handler class has @Extension + implements PlatformEventHandler
  4. For external: check the service is running at serviceBaseUrl
bash
# Publish a test event and watch logs
curl -X POST http://localhost:8080/api/workspaces/{wsId}/events/publish \
  -d '{"eventKey":"crm.deal.closed","payload":{"dealId":"test-123"}}'

Look for log lines containing Dispatching event and Delivered to.


ClassCastException in Capability Handler

Symptom: ClassCastException: ... cannot be cast to CapabilityResult.

Cause: Handler method returns a raw type instead of CapabilityResult<T>.

Fix:

java
// ❌ Wrong
public Map<String, Object> search(Map<String, Object> req, CapabilityContext ctx) {
    return Map.of("results", results);
}

// ✅ Correct
public CapabilityResult<List<ProductDto>> search(Map<String, Object> req, CapabilityContext ctx) {
    return CapabilityResult.success(results);
}

Webhook Returns 401

Symptom: External service webhook to /api/webhooks/{key} returns HTTP 401.

Cause: HMAC signature validation failed in WebhookHandler.handleWebhook().

Check:

  1. The webhook secret in your extension config matches what the external service uses
  2. You're computing HMAC over the raw request bytes (not the parsed JSON)
  3. The signature header name matches what you're reading (headers are lowercased)

JWT Authentication Fails (401 Unauthorized)

Symptom: API requests return 401 after login.

Check:

  1. The JWT cookie is included in the request (-b cookies.txt with curl)
  2. The METAONE_JWT_SECRET hasn't changed between login and the subsequent request
  3. The token hasn't expired (METAONE_JWT_EXPIRATION_MS, default 24h)

OAuth2 Login Redirect Fails

Symptom: Google OAuth2 login redirects to an error page.

Check: The redirect URI https://your-app/oauth2/callback/google must be:

  1. Listed in metaone.security.oauth2.authorized-redirect-uris
  2. Registered in your Google OAuth2 client in Google Cloud Console

FAQ

Q: Can an extension be installed in multiple workspaces?
A: Yes. Each workspace has its own ExtensionInstallation row and its own configuration. Extensions are workspace-isolated.

Q: Do I need to restart the platform to install a new extension?
A: No. Extensions are loaded at install time via PF4J. No restart is required.

Q: How do I debug an in-process extension?
A: Run the platform with remote debugging enabled:

bash
mvn spring-boot:run -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"

Then attach your IDE debugger to port 5005. Breakpoints in plugin code work normally.

Q: Can two extensions share data?
A: Extensions should not directly share database tables. Cross-extension communication happens via events (EventBusGateway.publish()) or capability invocations.

Q: What happens if an event handler throws an exception?
A: For async handlers, the exception is logged. For sync handlers, it propagates to the event publisher. Failed external deliveries go to dead_letter_event.

Q: How do I add a new workspace database?
A: The platform creates workspace databases automatically when a workspace is created, using the metaone.workspace.datasource.admin-url connection. Ensure the DB user has CREATE DATABASE privilege on the admin connection.

Q: Why do I see tenantId in some places and workspaceId in others?
A: Legacy naming. Both refer to the same concept — the workspace. The SDK event models use tenantId; host code uses workspaceId. A cleanup is tracked in TODOS P2.

Q: How do I add IDE support for PF4J plugins in IntelliJ?
A: Add metaone-sdk as a "provided" dependency in the plugin module POM. IntelliJ will resolve the SDK classes. Mark the plugins/ directory as excluded from indexing to avoid conflicts.

MetaOne Platform Documentation