nexu-io/open-design · warning · Error

project arg omitted and Open Design has no active project. T

Error message

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>".

What it means

Thrown by resolveProjectArg when GET /api/active succeeded but returned `active === false` or no `projectId`. The active context is a short-lived pointer to the project the user last interacted with in the Open Design UI, and it expires about 5 minutes after the last interaction. The MCP layer cannot guess which project to target without it.

Source

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

async function resolveProjectArg(
  baseUrl: string,
  arg: unknown,
  headers?: Record<string, string>,
): Promise<{ id: string; resolved: ResolvedProject | null; active: ActiveContext | null }> {
  if (typeof arg === 'string' && arg.length > 0) {
    const resolved = await resolveProjectId(baseUrl, arg, headers);
    return { id: resolved.id, resolved, active: null };
  }
  let active: ActiveContext;
  try {
    active = await getJson<ActiveContext>(`${baseUrl}/api/active`);
  } catch (err) {
    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) {

View on GitHub (pinned to 5be4028344)

Solutions

  1. Pass `project: "<id-or-name>"` explicitly so the call does not depend on UI activity.
  2. Have the user click into a project in the Open Design UI to refresh the active pointer, then retry.
  3. For automation/CI, always supply project explicitly and never rely on the active context.
  4. List projects first via the project MCP tool to discover the id, then pass it.

Example fix

// before
runStart({ prompt })  // relies on active context, expired
// after
runStart({ prompt, project: "my-deck" })
Defensive patterns

Strategy: validation

Validate before calling

async function ensureActive(baseUrl: string): Promise<string> {
  const a = await getJson(`${baseUrl}/api/active`);
  if (!a || a.active === false || !a.projectId) throw new Error('no active project; pass project explicitly');
  return a.projectId;
}

Try / catch

try {
  await runStart({ prompt });
} catch (e) {
  if (String(e.message).includes('no active project')) {
    await runStart({ prompt, project: await pickProject() });
  } else throw e;
}

Prevention

When it happens

Trigger: User closed or backgrounded Open Design for >5 min then invoked an MCP tool without `project`; fresh daemon where the user never opened a project; UI session expired; user switched projects but the active pointer lagged.

Common situations: Long-running agent tasks that outlive the active window; CI/headless runs with no UI user present; the user clicked away from the Open Design window.

Related errors


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