nexu-io/open-design · error · Error

project arg omitted and active context lookup failed: ${erro

Error message

project arg omitted and active context lookup failed: ${errorMessage(err)}. Pass project="<id-or-name>".

What it means

Thrown by resolveProjectArg when the caller omitted `project` and the fallback GET /api/active request itself failed (non-2xx or network error). This is an infrastructure/connectivity failure, not a validation error: the daemon was unreachable, refused, or errored on the active-context endpoint. The message embeds the underlying error via errorMessage(err).

Source

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

// When the agent omits `project`, fall back to whatever the user has
// open in Open Design. Returns the resolved id plus, for echo-back to the
// caller, the active-context payload that was used. Throws a clear
// error when neither is available so the agent can prompt the user
// rather than guessing.
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).');

View on GitHub (pinned to 5be4028344)

Solutions

  1. Start the daemon: run `pnpm tools-dev` and confirm `pnpm tools-dev status --json` reports it healthy.
  2. Verify the MCP base URL/port matches the running daemon's --daemon-port.
  3. Pass `project: "<id-or-name>"` explicitly to bypass the /api/active lookup entirely.
  4. Inspect the embedded errorMessage for the real HTTP status or ECONNREFUSED reason and address that.
  5. Ensure required headers (analyticsHeaders/auth) are supplied to the MCP client.

Example fix

// before
runStart({ prompt })  // no project, daemon down
// after
runStart({ prompt, project: projectUuid })  // explicit, skips /api/active
Defensive patterns

Strategy: retry

Validate before calling

async function daemonReachable(baseUrl: string): Promise<boolean> {
  try {
    const r = await fetch(`${baseUrl}/api/active`);
    return r.ok;
  } catch {
    return false;
  }
}

Try / catch

try {
  await runStart({ prompt });
} catch (e) {
  if (String(e.message).includes('active context lookup failed')) {
    await ensureDaemonUp();  // start tools-dev, then retry
    await runStart({ prompt, project: explicitId });
  } else throw e;
}

Prevention

When it happens

Trigger: Daemon not running; wrong base URL/port; auth/headers rejected by /api/active; daemon crashed mid-request; TLS or proxy interception on localhost; tools-dev not started.

Common situations: First-run without `pnpm tools-dev` started; pointing the MCP client at a stale port after a daemon restart; missing analytics/auth headers; firewall blocking the loopback port.

Related errors


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