nexu-io/open-design · warning · Error

unsupported resource URI: ${uri}

Error message

unsupported resource URI: ${uri}

What it means

The read_resource handler in mcp.ts accepts only od:// URIs of two shapes: od://skills/<id>/<path> and od://design-systems/<id>/<path> (plus an earlier od://active branch). Anything not matching the regex ^od://(skills|design-systems)/([^/]+)/(.+)$ throws.

Source

Thrown at apps/daemon/src/mcp.ts:1857

    if (uri === 'od://focus/active') {
      const result = await daemonTarget.call('read_resource', {}, async (baseUrl) =>
        ok(await getJson<ActiveContext>(`${baseUrl}/api/active`)),
      );
      if (result.isError === true) throw new Error(result.content[0]?.text);
      const data = parseMcpResult(result);
      return {
        contents: [
          {
            uri,
            mimeType: 'application/json',
            text: JSON.stringify(data, null, 2),
          },
        ],
      };
    }
    const m = String(uri || '').match(/^od:\/\/(skills|design-systems)\/([^/]+)\/(.+)$/);
    if (!m) {
      throw new Error(`unsupported resource URI: ${uri}`);
    }
    const [, kind, id] = m as [string, 'skills' | 'design-systems', string, string];
    const route = kind === 'skills' ? 'skills' : 'design-systems';
    const result = await daemonTarget.call('read_resource', {}, async (baseUrl) =>
      ok(await getJson<ResourcePayload>(
        `${baseUrl}/api/${route}/${encodeURIComponent(decodeURIComponent(id))}`,
      )),
    );
    if (result.isError === true) throw new Error(result.content[0]?.text);
    const data = parseMcpResult(result) as ResourcePayload | null;
    const text =
      data?.skill?.body ??
      data?.skill?.content ??
      data?.designSystem?.body ??
      data?.designSystem?.content ??
      data?.body ??
      data?.content ??
      '';

View on GitHub (pinned to 5be4028344)

Solutions

  1. Use od://skills/<id>/<path> or od://design-systems/<id>/<path>.
  2. For active context use od://active.
  3. Ensure both the id and the trailing path segment are present.

Example fix

// before
read_resource('https://example.com/skills/foo')

// after
read_resource('od://skills/foo/SKILL.md')
Defensive patterns

Strategy: type-guard

Validate before calling

const RE = /^od:\/\/(skills|design-systems)\/([^/]+)\/(.+)$/;
if (uri !== 'od://active' && !RE.test(uri)) {
  throw new Error(`unsupported resource URI: ${uri}`);
}

Type guard

function isOdResourceUri(u: unknown): boolean {
  return typeof u === 'string'
    && (u === 'od://active'
      || /^od:\/\/(skills|design-systems)\/([^/]+)\/(.+)$/.test(u));
}

Prevention

When it happens

Trigger: A client requests a non-od:// URI; a malformed od:// URI; an od:// kind other than skills or design-systems; a URI missing the id or trailing path segment.

Common situations: An external agent enumerated resources from elsewhere and tried to read them here; the URI was truncated.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/8cb1ff8079bdca6d. Report an issue: GitHub.