paperclipai/paperclip · error

Plugin UI is not available (status: ${plugin.status})

Error message

Plugin UI is not available (status: ${plugin.status})

What it means

Returned as HTTP 403 by the plugin UI static route (GET /_plugins/:pluginId/ui/*, server/src/routes/plugin-ui-static.ts:271). The route resolves the plugin by UUID or key, but only serves its UI bundle when the registry status is exactly 'ready'. Any other status ('installed', 'disabled', 'error', 'upgrade_pending', 'uninstalled') means the bundle on disk is not guaranteed complete, so serving is refused.

Source

Thrown at server/src/routes/plugin-ui-static.ts:271

          ? (error as { code?: unknown }).code
          : undefined;
      if (maybeCode !== "22P02") {
        throw error;
      }
    }
    if (!plugin) {
      plugin = await registry.getByKey(pluginId);
    }

    if (!plugin) {
      res.status(404).json({ error: "Plugin not found" });
      return;
    }

    // Step 2: Verify the plugin is ready and has UI declared
    if (plugin.status !== "ready") {
      res.status(403).json({
        error: `Plugin UI is not available (status: ${plugin.status})`,
      });
      return;
    }

    const manifest = plugin.manifestJson;
    if (!manifest?.entrypoints?.ui) {
      res.status(404).json({ error: "Plugin does not declare a UI bundle" });
      return;
    }

    const rawCompanyId = req.query.companyId;
    if (
      Array.isArray(rawCompanyId) ||
      (rawCompanyId !== undefined && typeof rawCompanyId !== "string")
    ) {
      throw badRequest('"companyId" must be a string when provided');
    }
    const companyId = typeof rawCompanyId === "string" ? rawCompanyId.trim() : "";

View on GitHub (pinned to 120ae5428f)

Solutions

  1. Check current status with GET /api/plugins (or GET /api/plugins?status=ready) and only load UI assets once the plugin reports 'ready'
  2. If status is 'error', inspect the install logs and re-run the install/repair flow to move it back to 'ready'
  3. If status is 'disabled' or 'uninstalled', re-enable or reinstall the plugin via the plugins API before loading its UI
  4. In host UI code, gate the dynamic import of the plugin UI entry on plugin.status === 'ready', and on a 403 re-fetch the plugin record instead of blindly retrying the asset URL

Example fix

// before
const mod = await import(`/_plugins/${pluginId}/ui/${entry}`);

// after
const plugin = await api.getPlugin(pluginId);
if (plugin.status !== "ready") {
  throw new Error(`Plugin ${pluginId} not ready (status: ${plugin.status})`);
}
const mod = await import(`/_plugins/${pluginId}/ui/${entry}`);
Defensive patterns

Strategy: validation

Validate before calling

const res = await fetch(`/api/plugins/${encodeURIComponent(pluginId)}`);
const plugin = await res.json();
if (plugin.status !== "ready") {
  // Defer loading UI assets until the plugin is ready
  throw new Error(`Plugin not ready: ${plugin.status}`);
}

Type guard

const isPluginReady = (
  p: { status: string },
): p is { status: "ready" } => p.status === "ready";

Try / catch

const assetRes = await fetch(url);
if (assetRes.status === 403) {
  const { error } = await assetRes.json();
  if (error.startsWith("Plugin UI is not available")) {
    // Re-fetch plugin status and back off instead of retrying the asset
  }
}

Prevention

When it happens

Trigger: Requesting GET /_plugins/<pluginId>/ui/index.js (or any asset) while GET /api/plugins shows the plugin in a non-ready state: install still in flight ('installed'), install/build failed ('error'), mid-upgrade ('upgrade_pending'), disabled, or uninstalled. Typical when the UI tries to dynamically import the plugin bundle immediately after an install API call returns, before the registry flips the row to 'ready'.

Common situations: Polling a freshly installed plugin's UI too early; plugin card still rendered in the board after the plugin was disabled or uninstalled; an upgrade left the plugin in 'upgrade_pending'; install failed (status 'error') but a stale browser tab keeps requesting its assets.

Related errors


AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18). Data as JSON: /api/errors/614e8614ccbb8cff. Report an issue: GitHub.