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
- If you are an end user, pass a non-empty `project` string (name or UUID).
- If you are an internal caller, validate/guard the value before invoking resolveProjectId, or route through resolveProjectArg instead.
- 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
- Pre-validate the project arg at your boundary before calling daemon helpers.
- Prefer resolveProjectArg over resolveProjectId in app code.
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
- existing config at ${where} is not valid JSON: ${err instanc
- existing config at ${where} is not a JSON object
- pluginWorkflowId requires a validated externalPluginContext
- The Open Design brief is incomplete. Missing: ${missing.join
- artifactType must be one of: ${Object.keys(openDesignBriefCa
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/f80f1fb36c00df61.
Report an issue: GitHub.