n8n-io/n8n · error · Error

Could not determine personal project ID

Error message

Could not determine personal project ID

What it means

getPersonalProjectId GETs /rest/projects/personal and expects result.data.id. If the response is missing data.id, the client cannot address project-scoped resources (data tables, etc.). Usually a sign the call was unauthenticated, the user has no personal project, or the response shape changed.

Source

Thrown at packages/@n8n/instance-ai/evaluations/clients/n8n-client.ts:833

	): Promise<void> {
		await this.fetch('/rest/instance-ai/eval/seed-data-table-rows', {
			method: 'POST',
			body: { threadId, tableId, rows },
		});
	}

	// -- Data tables ---------------------------------------------------------

	/**
	 * Get the personal project ID for the authenticated user.
	 * GET /rest/projects/personal
	 */
	async getPersonalProjectId(): Promise<string> {
		const result = (await this.fetch('/rest/projects/personal')) as {
			data: { id: string };
		};
		if (!result.data?.id) {
			throw new Error('Could not determine personal project ID');
		}
		return result.data.id;
	}

	/**
	 * List data tables in a project.
	 * GET /rest/projects/:projectId/data-tables
	 */
	async listDataTables(projectId: string): Promise<Array<{ id: string; name: string }>> {
		// The list endpoint paginates: `{ data: { count, data: [...] } }`. Reading
		// `result.data` as the array made this return [] for every project, silently —
		// it has no error path, so every caller just saw "no tables".
		//
		// `take` is explicit because the default page is 10, and every caller here
		// enumerates the WHOLE set (seed eviction, CU cleanup, discovery's pre-existing
		// set) — a short page silently leaves leftovers behind. 250 is the server's
		// per-page cap; a case declares at most 20 tables and eviction runs before
		// every build, so the backlog drains rather than outgrowing one page.

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Ensure login() (or acceptInvitation) completed and the cookie is set before calling getPersonalProjectId.
  2. Confirm the user has a personal project (create/finish onboarding if not).
  3. Inspect the raw /rest/projects/personal response to verify the envelope shape.
Defensive patterns

Strategy: validation

Validate before calling

if (!client.sessionCookie) throw new Error('not logged in');
const r = (await client.fetch('/rest/projects/personal')) as { data?: { id?: string } };
if (!r.data?.id) throw new Error('no personal project; finish user onboarding');

Type guard

const hasPersonalProject = (v: unknown): v is { data: { id: string } } =>
  typeof v === 'object' && v !== null && typeof (v as any)?.data?.id === 'string';

Try / catch

try { return await client.getPersonalProjectId(); }
catch (e) {
  if (e instanceof Error && e.message.includes('personal project')) { /* re-login or provision user */ }
  else throw e;
}

Prevention

When it happens

Trigger: Calling getPersonalProjectId before login(); user provisioning incomplete so no personal project exists; backend version with a different response envelope.

Common situations: Forgetting to await login(); freshly invited user whose project wasn't provisioned; proxy returning an error envelope without data.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/893d3038d5a18b4c. Report an issue: GitHub.