Hmbown/CodeWhale · error
resource " " is not part of the bundled skill pack —…
Error message
resource "${params?.uri ?? ""}" is not part of the bundled skill pack — resources/list names the readable URIs What it means
The computer-use MCP server implements resources/read by looking up the requested URI in the bundled `skillPack` array (the files exposed via resources/list). If no entry matches the given `params.uri`, it throws a paramError explaining that the resource is not part of the bundled skill pack and that resources/list names the readable URIs. It is a strict allow-list: only URIs exactly matching a bundled file can be read.
Solutions
- Call resources/list first and copy a uri from its response verbatim into resources/read.
- Check the client's cached URIs against the current server version — regenerate them from resources/list if the skill pack changed.
- Ensure the params object actually includes a `uri` field with the exact string (no path-style substitution, no case/trailing-slash differences).
- If the needed resource genuinely should exist, rebuild/update the plugin so skillPack contains it.
Example fix
// before
await mcp.request({ method: "resources/read", params: { uri: "docs/getting-started.md" } });
// after
const { resources } = await mcp.request({ method: "resources/list" });
const wanted = resources.find((r) => r.name === "getting-started.md");
await mcp.request({ method: "resources/read", params: { uri: wanted.uri } }); Defensive patterns
Strategy: validation
Validate before calling
// Before calling resources/read:
const { resources } = await client.listResources();
const readable = new Set(resources.map((r) => r.uri));
if (!readable.has(myUri)) {
throw new Error(`URI "${myUri}" not offered; use one of: ${[...readable].join(", ")}`);
} Type guard
function isKnownResourceUri(uri, resources) {
return typeof uri === 'string' && resources.some((r) => r.uri === uri);
} Try / catch
try {
const res = await client.readResource({ uri });
} catch (err) {
if (String(err.message).includes('not part of the bundled skill pack')) {
const { resources } = await client.listResources();
// re-read with resources[0].uri or surface the valid URIs to the user
} else throw err;
} Prevention
- Always list resources before reading; never hard-code URIs.
- Invalidate cached URIs after any plugin/server update.
- Match URIs exactly — no case, slash, or scheme improvisation.
- Pass an explicit uri string in params; a missing uri silently becomes "" and fails.
When it happens
Trigger: An MCP client calls resources/read with a uri that is not in skillPack: a stale URI cached from an older server version, a guessed/constructed URI, a file path instead of the pack's URI scheme, an omitted uri (params undefined yields "" in the message), or a URI with different casing/whitespace than the exact registered one.
Common situations: A client hard-coded resource URIs from an older plugin version before the skill pack changed; a developer tests resources/read with a local file path like /docs/skill.md instead of the pack's canonical URI; an LLM-driven client hallucinates a plausible URI; the request omits params entirely so uri falls back to the empty string.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- skill " " is unknown — skills/list names the catalog
- bad_target
- .annotations is not a valid MCP annotations object
- .inputSchema must be a valid object-shaped MCP input schema
- .size must be an integer
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/c2ff1bbfa539e4b7.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/mcp/server.mjs:1273
experimental: { "io.modelcontextprotocol/skills": {} },
},
serverInfo: { name: SERVER_NAME, version: APP_VERSION, platforms: ["darwin", "win32", "linux", "harmonyos"], transports: ["local", "ssh", "hdc"] },
};
},
"tools/list"() {
// The advertised surface is what every session pays for; merged-away wire
// names stay callable as aliases but are never listed. A capability grant
// narrows the listing further, never widens it.
const advertised = TOOLS.filter((t) => t.hidden !== true);
if (!GRANT) return { tools: advertised };
return { tools: advertised.filter((t) => t.name === "stop_computer_control" || GRANT.has(t.name) || (MERGED_EXPANSION[t.name] ?? []).some((wire) => GRANT.has(wire))) };
},
"resources/list"() {
return { resources: skillPack.map(({ uri, rel, mime, size }) => ({ uri, name: rel, mimeType: mime, size })) };
},
"resources/read"(params) {
const file = skillPack.find((f) => f.uri === params?.uri);
if (!file) throw paramError(`resource "${params?.uri ?? ""}" is not part of the bundled skill pack — resources/list names the readable URIs`);
return { contents: [{ uri: file.uri, mimeType: file.mime, text: file.text }] };
},
"skills/list"() {
return {
skills: [{
uri: SKILL_ROOT_URI, name: SKILL_NAME, description: SKILL_DESCRIPTION,
files: skillPack.map(({ uri, sha256, size }) => ({ uri, sha256, bytes: size })),
}],
};
},
"skills/get"(params) {
const entry = skillPack.find((f) => f.uri === (params?.uri ?? SKILL_ROOT_URI));
if (!entry) throw paramError(`skill "${params?.uri ?? ""}" is unknown — skills/list names the catalog`);
return {
skill: { uri: entry.uri, name: SKILL_NAME, description: SKILL_DESCRIPTION, frontmatter: entry.frontmatter, content: entry.text },
manifest: skillPack.map(({ uri, sha256, size }) => ({ uri, sha256, bytes: size })),
};
},View on GitHub (pinned to 73e0f67d83)