thedotmack/claude-mem · error

project_id must belong to team_id

Error message

project_id must belong to team_id

What it means

Thrown by assertProjectOwnership (shared helper in utils.ts). It queries projects by (id=projectId, team_id=teamId); a missing row means the project is not owned by that team. This is the foundational tenancy check called at the top of create() paths across repositories.

Source

Thrown at src/storage/postgres/utils.ts:62

  text: string,
  values: unknown[] = []
): Promise<T | null> {
  const result = await client.query<T>(text, values);
  return result.rows[0] ?? null;
}

export async function assertProjectOwnership(
  client: PostgresQueryable,
  projectId: string,
  teamId: string
): Promise<void> {
  const row = await queryOne<{ id: string }>(
    client,
    'SELECT id FROM projects WHERE id = $1 AND team_id = $2',
    [projectId, teamId]
  );
  if (!row) {
    throw new Error('project_id must belong to team_id');
  }
}

export async function assertSessionOwnership(
  client: PostgresQueryable,
  serverSessionId: string,
  projectId: string,
  teamId: string
): Promise<void> {
  const row = await queryOne<{ id: string }>(
    client,
    'SELECT id FROM server_sessions WHERE id = $1 AND project_id = $2 AND team_id = $3',
    [serverSessionId, projectId, teamId]
  );
  if (!row) {
    throw new Error('server_session_id must belong to project_id and team_id');
  }
}

View on GitHub (pinned to d768ba3643)

Solutions

  1. Verify (projectId, teamId) pairing against projects before calling repository methods.
  2. Derive teamId from the authenticated session/request context and ensure projectId belongs to it.
  3. Use the exact project id returned by project creation.

Example fix

// before
await observations.create({ projectId, teamId, content });
// after
await assertProjectOwnership(client, projectId, teamId); // or a guard that returns boolean
if (!await projectExists(projectId, teamId)) throw new Error('project not in team');
await observations.create({ projectId, teamId, content });
Defensive patterns

Strategy: validation

Validate before calling

async function projectExists(client, projectId, teamId){ const r = await client.query('SELECT 1 FROM projects WHERE id=$1 AND team_id=$2', [projectId, teamId]); return r.rowCount>0; }

Prevention

When it happens

Trigger: Calling any repository create/validate method with a projectId that is not a row in projects for the given teamId. Fires before source-specific checks because assertProjectOwnership runs first.

Common situations: Wrong team context, project id from another team, deleted project, id typo, or thread/request-local team id not set correctly.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/6125ca6f71e5594a. Report an issue: GitHub.