nexu-io/open-design · error · Error

project is required (string).

Error message

project is required (string).

What it means

Thrown by resolveProjectId, the lower-level resolver reached only after the caller explicitly supplied a `project` arg. It requires a non-empty string. Because the higher-level resolveProjectArg only forwards truthy strings to resolveProjectId, hitting this directly usually means an internal caller invoked resolveProjectId with undefined/empty, or a code path bypassed the arg guard.

Source

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

    throw new Error(
      `project arg omitted and active context lookup failed: ${errorMessage(err)}. Pass project="<id-or-name>".`,
    );
  }
  if (!active || active.active === false || !active.projectId) {
    throw new Error(
      'project arg omitted and Open Design has no active project. The active context expires about 5 minutes after the last user interaction with Open Design - the user may need to click into a project to wake it up. Otherwise pass project="<id-or-name>".',
    );
  }
  return { id: active.projectId, resolved: null, active };
}

async function resolveProjectId(
  baseUrl: string,
  arg: unknown,
  headers?: Record<string, string>,
): Promise<ResolvedProject> {
  if (typeof arg !== 'string' || !arg) {
    throw new Error('project is required (string).');
  }
  if (UUID_RE.test(arg)) return { id: arg, name: arg, source: 'uuid' as const };

  const list = await fetchProjectList(baseUrl, headers);
  if (list.length === 0) {
    throw new Error('no projects on this daemon');
  }

  const lower = arg.toLowerCase();
  const norm = (s: unknown): string =>
    String(s || '')
      .toLowerCase()
      .replace(/\s*\(\d+\)\s*$/, '')
      .replace(/[\s_-]+/g, '-');
  const target = norm(arg);

  const idMatch = list.find((p) => p.id === arg);
  if (idMatch) return { id: idMatch.id, name: idMatch.name, source: 'id' as const };

View on GitHub (pinned to 5be4028344)

Solutions

  1. If you are an end user, pass a non-empty `project` string (name or UUID).
  2. If you are an internal caller, validate/guard the value before invoking resolveProjectId, or route through resolveProjectArg instead.
  3. Treat this error as a bug if it surfaces from a documented MCP tool — file an issue with the call args.

Example fix

// before
resolveProjectId(baseUrl, maybeEmpty, headers)
// after
if (typeof arg !== 'string' || !arg) throw new Error('project is required (string).');
resolveProjectId(baseUrl, arg, headers)
Defensive patterns

Strategy: validation

Validate before calling

if (typeof project !== 'string' || project.trim() === '') {
  throw new Error('project is required (string).');
}

Type guard

function isNonEmptyString(v: unknown): v is string {
  return typeof v === 'string' && v.length > 0;
}

Prevention

When it happens

Trigger: An internal daemon/MCP code path calls resolveProjectId directly with an undefined/empty value; a typed boundary let a non-string slip through; refactoring removed the upstream truthy-string check.

Common situations: Code refactor that introduces a new call site of resolveProjectId without pre-validation; passing a value typed as `any` that is actually undefined.

Related errors


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