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
- Verify `.claude-plugin/plugin.json` exists under the given dir at that ref on GitHub.
- Drop the <dir> segment and let the resolver auto-locate the plugin root (works when the repo has exactly one plugin).
- Point the URL at the actual plugin root directory containing .claude-plugin/.
- 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
- Point the URL at the directory containing .claude-plugin/, not a nested subdir.
- Omit <dir> for single-plugin repos.
- Verify the file exists at the specific ref, not just on the default branch.
- Keep the .claude-plugin directory intact when vendoring.
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
- plugin_ref_not_found
- plugin_ambiguous
- invalid_plugin_manifest
- Workspace path is unavailable; attachments could not be copi
- plugin_empty
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/9609e5a9e2bcc934.
Report an issue: GitHub.