thedotmack/claude-mem · error · Error

observation_reindex source_id must belong to project_id and…

Error message

observation_reindex source_id must belong to project_id and team_id

What it means

For 'observation_reindex' sources without a generationJobId, assertObservationOwnership verifies the sourceId refers to an existing observation row in the same project and team. The id is expected to point at an observations row; if it does not, reindex provenance cannot be established and the error is thrown.

Solutions

  1. Confirm sourceId is an observations id visible with the given projectId/teamId.
  2. Re-create the observation if it was deleted, or skip reindexing it.
  3. Ensure the reindex job reads ids and tenant ids from the same database it writes to.

Example fix

// before
await repo.addSource({ sourceType: 'observation_reindex', sourceId: sessionId, ... });
// after
await repo.addSource({ sourceType: 'observation_reindex', sourceId: observation.id, ... });
Defensive patterns

Strategy: validation

Validate before calling

if (sourceType === 'observation_reindex' && !generationJobId) {
  const ok = await db.oneOrNone(
    'SELECT 1 FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',
    [sourceId, projectId, teamId]
  );
  if (!ok) throw new Error(`reindex source ${sourceId} is not an in-scope observation`);
}

Try / catch

try {
  await repo.addSource(input);
} catch (e) {
  if (e instanceof Error && e.message === 'observation_reindex source_id must belong to project_id and team_id') {
    logger.warn('skipping reindex of missing observation', { id: input.sourceId });
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: addSource with sourceType='observation_reindex', no generationJobId, and sourceId set to an id that is not an observations row in scope (a session id, an agent event id, a deleted observation, or wrong tenant).

Common situations: Reindex jobs that pass the wrong id field; observations deleted between listing and reindexing; running the reindex against a different database than the one the ids came from.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/ca3b1e9dc4b55c05. Report an issue: GitHub.

Appendix: source

Thrown at src/storage/postgres/observations.ts:388

  );
  if (!row) {
    throw new Error('agent_event_id must belong to project_id and team_id');
  }
}

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

function mapObservationRow(row: ObservationRow): PostgresObservation {
  return {
    id: row.id,
    projectId: row.project_id,
    teamId: row.team_id,
    serverSessionId: row.server_session_id,
    kind: row.kind,
    content: row.content,
    generationKey: row.generation_key,
    metadata: toJsonObject(row.metadata),
    embedding: row.embedding,
    createdByJobId: row.created_by_job_id,
    createdAtEpoch: toEpoch(row.created_at),
    updatedAtEpoch: toEpoch(row.updated_at)
  };

View on GitHub (pinned to d8bc9755e7)