nexu-io/open-design · error · Error
project not found: ${projectId}
Error message
project not found: ${projectId} What it means
getProject(db, projectId) returned null inside finalizeDesignPackage. This is defensive: the route handler validates the project exists and returns 404 before reaching this function. It is retained for direct (non-HTTP) callers such as CLI scripts. When reached, it surfaces as a generic Error which the route maps to 500 INTERNAL_ERROR.
Source
Thrown at apps/daemon/src/design/finalize-design.ts:274
manifest: newest.artifactManifest ?? null,
};
}
return null;
}
export async function finalizeDesignPackage(
db: Db,
projectsRoot: string,
designSystemsRoot: string,
projectId: string,
options: FinalizeOptions,
): Promise<FinalizeAnthropicResponse> {
const project = getProject(db, projectId);
if (!project) {
// Defensive — the route handler validates this and returns 404 before
// reaching here. Kept for direct (non-HTTP) callers, e.g. CLI scripts.
throw new Error(`project not found: ${projectId}`);
}
// Imported-folder projects (created via /api/import/folder) carry
// `metadata.baseDir` and write to the user's actual folder rather than
// `.od/projects/<id>`. resolveProjectDir handles both shapes; calling
// bare `projectDir` would silently land DESIGN.md in the hidden daemon
// data dir for these projects (PR #832 P1 finding from @lefarcen).
const projectMetadata = (project as { metadata?: { baseDir?: string } | null }).metadata ?? null;
const dir = resolveProjectDir(projectsRoot, projectId, projectMetadata ?? undefined);
// For imported-folder projects, `dir` is the user's own directory and
// already exists; mkdirSync is a no-op (recursive:true is idempotent).
// For native projects, it lazily creates `.od/projects/<id>`.
fs.mkdirSync(dir, { recursive: true });
const finalPath = path.join(dir, OUTPUT_FILENAME);
const lockPath = path.join(dir, LOCK_FILENAME);
const tmpPath = path.join(
dir,
`${OUTPUT_FILENAME}.tmp.${process.pid}.${randomBytes(4).toString('hex')}`,View on GitHub (pinned to 5be4028344)
Solutions
- Verify the project exists with getProject(db, projectId) before calling finalizeDesignPackage.
- Prefer the HTTP endpoint POST /api/projects/:id/finalize, which validates existence and returns 404 cleanly.
- Re-check the projectId spelling / source if it came from a stale cache.
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the project exists before calling finalizeDesignPackage directly.
import { getProject } from '../db.js';
if (!getProject(db, projectId)) {
throw new Error(`project not found: ${projectId}`);
}
const result = await finalizeDesignPackage(db, projectsRoot, dsRoot, projectId, options); Type guard
function projectExists(db: Db, id: string): boolean {
return getProject(db, id) !== null;
} Try / catch
try {
await finalizeDesignPackage(db, projectsRoot, dsRoot, projectId, options);
} catch (err) {
if (String(err).startsWith('project not found:')) {
// direct (non-HTTP) caller: surface a clean 404-equivalent
}
throw err;
} Prevention
- Prefer the HTTP finalize endpoint, which returns a clean 404 before reaching this code.
- For CLI/automation, fetch and validate the project row first.
- Refresh cached project IDs after deletions.
When it happens
Trigger: Calling finalizeDesignPackage directly (not via the HTTP route) with a projectId that has no row in the projects table; or a race where the project was deleted between the route's existence check and the call.
Common situations: A CLI/automation script referencing a deleted or mistyped project ID; a stale project ID cached client-side; the project was removed in another tab between the 404 pre-check and the call (rare).
Related errors
- brand not found: ${id}
- brand.json is not valid JSON.
- brand.json failed validation: ${errorMessage(err)}
- Project ${projectId} not found
- --image not found: ${rel}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/cf8303e71de74c91.
Report an issue: GitHub.