thedotmack/claude-mem · error
agent_event source_id must belong to project_id and team_id
Error message
agent_event source_id must belong to project_id and team_id
What it means
Thrown by validateSource when creating/scoping an observation_generation_job with sourceType 'agent_event' but the referenced agent_event row doesn't exist for the given eventId+project_id+team_id, or when sourceId was provided alongside a differing agentEventId. This enforces tenancy: the source event must belong to the same project and team as the job.
Source
Thrown at src/storage/postgres/generation-jobs.ts:261
private async validateSource(input: {
projectId: string;
teamId: string;
sourceType: ObservationGenerationJobSourceType;
sourceId: string;
agentEventId?: string | null;
serverSessionId?: string | null;
}): Promise<void> {
await assertProjectOwnership(this.client, input.projectId, input.teamId);
if (input.sourceType === 'agent_event') {
const eventId = input.agentEventId ?? input.sourceId;
const row = await queryOne<{ id: string; server_session_id: string | null }>(
this.client,
'SELECT id, server_session_id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',
[eventId, input.projectId, input.teamId]
);
if (!row || input.sourceId !== eventId) {
throw new Error('agent_event source_id must belong to project_id and team_id');
}
if (input.serverSessionId) {
await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);
if (row.server_session_id && row.server_session_id !== input.serverSessionId) {
throw new Error('server_session_id must match the agent_event server_session_id');
}
}
return;
}
if (input.sourceType === 'session_summary') {
const sessionId = input.serverSessionId ?? input.sourceId;
await assertSessionOwnership(this.client, sessionId, input.projectId, input.teamId);
if (input.sourceId !== sessionId) {
throw new Error('session_summary source_id must equal server_session_id');
}
return;
}View on GitHub (pinned to d768ba3643)
Solutions
- Verify the agent_event exists for the same project_id and team_id before creating the job (SELECT id FROM agent_events WHERE id=? AND project_id=? AND team_id=?).
- Ensure sourceId and agentEventId refer to the same event (or omit agentEventId when it equals sourceId).
- Confirm project_id/team_id on the request match the event's ownership; reject cross-tenant references early.
- If the event was deleted, either re-create it or choose a valid source.
Example fix
// before
await repo.create({ sourceType: 'agent_event', sourceId, agentEventId: otherId, projectId, teamId });
// after
const eventId = agentEventId ?? sourceId;
if (sourceId !== eventId) throw new Error('sourceId must match agentEventId');
const owned = await queryOne('SELECT id FROM agent_events WHERE id=$1 AND project_id=$2 AND team_id=$3', [eventId, projectId, teamId]);
if (!owned) throw new Error('agent_event not owned by project/team');
await repo.create({ sourceType: 'agent_event', sourceId, projectId, teamId }); Defensive patterns
Strategy: validation
Validate before calling
// Pre-validate ownership and id consistency
const eventId = input.agentEventId ?? input.sourceId;
if (input.sourceId !== eventId) throw new Error('sourceId must equal agentEventId');
const row = await queryOne(
'SELECT id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',
[eventId, input.projectId, input.teamId]
);
if (!row) throw new Error('agent_event not owned by project/team'); Try / catch
try {
await repo.create(input);
} catch (err) {
if (err instanceof Error && /source_id must belong to project_id and team_id/.test(err.message)) {
return res.status(400).json({ error: 'Agent event not found for this project/team' });
}
throw err;
} Prevention
- Always scope source lookups by project_id and team_id to enforce tenancy.
- Keep sourceId and agentEventId consistent (or omit agentEventId when equal).
- Validate the agent_event exists before creating the job to give a clear 400.
When it happens
Trigger: Job creation/input with sourceType 'agent_event' where (eventId = agentEventId ?? sourceId) has no row in agent_events for that project/team, or input.sourceId !== eventId (the two identifiers disagree).
Common situations: Caller passed an agentEventId from a different project/team (cross-tenant); typo in sourceId/agentEventId; the agent_event was deleted before job creation; mixing sourceId and agentEventId inconsistently.
Related errors
- project_id must belong to team_id
- observation generation job status transition was not applied
- server_session_id must match the agent_event server_session_
- observation_reindex source_id must belong to project_id and
- observation_id does not exist
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/543b9e80d66fb088.
Report an issue: GitHub.