different-ai/openwork · error · ApiError

plugin_manifest_not_found

plugin_manifest_not_found

Error message

No .claude-plugin/plugin.json found under ${normalized}/

What it means

locatePluginRoot finds the directory containing .claude-plugin/plugin.json within the resolved repo tree. When an explicit dir is given, it requires that exact `<dir>/.claude-plugin/plugin.json` entry to exist in the tree; otherwise it throws this 404 ApiError naming the normalized directory.

Source

Thrown at apps/server/src/claude-plugin-bundle.ts:210

    } catch (error) {
      lastError = error;
    }
  }
  if (lastError instanceof Error) throw lastError;
  throw new ApiError(404, "plugin_ref_not_found", "Could not resolve the requested branch or tag");
}

// Find the plugin root: the given subdir, the repo root, or the shallowest
// directory containing `.claude-plugin/plugin.json`.
function locatePluginRoot(tree: TreeEntry[], dir: string | null): string {
  const manifestPaths = tree
    .map((entry) => entry.path)
    .filter((path) => path === ".claude-plugin/plugin.json" || path.endsWith("/.claude-plugin/plugin.json"));
  if (dir) {
    const normalized = dir.replace(/\/+$/, "");
    const expected = `${normalized}/.claude-plugin/plugin.json`;
    if (!manifestPaths.includes(expected)) {
      throw new ApiError(404, "plugin_manifest_not_found", `No .claude-plugin/plugin.json found under ${normalized}/`);
    }
    return `${normalized}/`;
  }
  if (manifestPaths.length === 0) {
    throw new ApiError(404, "plugin_manifest_not_found", "No .claude-plugin/plugin.json found in this repository");
  }
  manifestPaths.sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b));
  const shallowest = manifestPaths[0]!;
  const root = shallowest.slice(0, shallowest.length - ".claude-plugin/plugin.json".length);
  const sameDepth = manifestPaths.filter((path) => path.split("/").length === shallowest.split("/").length);
  if (sameDepth.length > 1) {
    const candidates = sameDepth
      .map((path) => path.slice(0, path.length - "/.claude-plugin/plugin.json".length))
      .join(", ");
    throw new ApiError(400, "plugin_ambiguous", `Multiple plugins found (${candidates}). Add the plugin directory to the URL, e.g. /tree/main/<dir>.`);
  }
  return root;
}

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Verify `.claude-plugin/plugin.json` exists under the given dir at that ref on GitHub.
  2. Drop the <dir> segment and let the resolver auto-locate the plugin root (works when the repo has exactly one plugin).
  3. Point the URL at the actual plugin root directory containing .claude-plugin/.
  4. Check the ref: the manifest may exist on another branch/tag but not the one requested.

Example fix

// before
GET /plugin/tree/main/myplugin/src   // manifest is at myplugin/.claude-plugin/plugin.json
// after
GET /plugin/tree/main/myplugin       // point at the plugin root
Defensive patterns

Strategy: validation

Validate before calling

// verify the manifest path exists at the ref before calling
const p = await fetch(`https://api.github.com/repos/${owner}/${repo}/contents/${dir}/.claude-plugin/plugin.json?ref=${ref}`);
if (!p.ok) throw new Error(`missing ${dir}/.claude-plugin/plugin.json at ${ref}`);

Try / catch

try {
  await installPlugin(url);
} catch (e) {
  if (isApiError(e) && e.code === "plugin_manifest_not_found") {
    // retry without the dir segment to auto-locate the plugin root
  }
}

Prevention

When it happens

Trigger: Requesting /tree/<ref>/<dir> where dir exists but does not contain .claude-plugin/plugin.json (wrong subdir, manifest at a different level, or manifest filename misspelled).

Common situations: Plugin manifest lives at repo root but a subdirectory was passed; plugin restructured so the manifest moved to a nested path; passing the parent directory instead of the plugin root directory; `.claude-plugin` folder omitted when vendoring the plugin.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/9609e5a9e2bcc934. Report an issue: GitHub.