thedotmack/claude-mem · error · 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
validateSource enforces that the agent_event a generation job references actually exists and is scoped to the same project_id and team_id as the job being created. If the SELECT returns no row, or input.sourceId differs from the validated eventId, creation is aborted. This prevents cross-project/cross-team data leakage through generation-job sources.
Solutions
- Confirm the agent_event exists in the same project_id and team_id as the generation job before creating it
- Query the event scoped to your project/team first and use its id as sourceId verbatim
- Check you are not mixing tenant contexts (team_id/project_id from different requests)
- If the event was deleted, re-create or re-emit the event before creating the generation job
Example fix
// before
await jobs.create({ sourceType: 'agent_event', sourceId: eventId, projectId: 'p1', teamId: 't1' });
// after
const event = await eventsRepository.getByIdForScope(eventId, 'p1', 't1');
if (!event) throw new Error('event not found for scope');
await jobs.create({ sourceType: 'agent_event', sourceId: event.id, projectId: 'p1', teamId: 't1' }); Defensive patterns
Strategy: validation
Validate before calling
const event = await client.query('SELECT id FROM agent_events WHERE id=$1 AND project_id=$2 AND team_id=$3', [eventId, projectId, teamId]);
if (event.rowCount === 0) throw new Error('agent_event not found in scope'); Try / catch
try {
await jobs.create(input);
} catch (err) {
if (err instanceof Error && err.message.includes('agent_event source_id must belong')) {
throw new ScopeError('Referenced agent_event does not exist in this project/team');
}
throw err;
} Prevention
- Always resolve source ids through scoped (project/team-filtered) queries
- Never cache event ids across team/project switches
- Validate ids belong to the current tenant in your API layer before hitting the repository
When it happens
Trigger: Calling create() with sourceType 'agent_event' where the source_id does not exist in agent_events, or exists under a different project_id/team_id, or the supplied sourceId string does not match the event id used for the ownership query.
Common situations: Client passes an event id from another tenant; stale event id after a data deletion; mixing up sourceId with another field in the request payload; multi-team setup where the caller cached ids from a previous team context.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- observation_reindex source_id must belong to project_id and…
- generation_job_id must belong to project_id and team_id
- server_session_id must match the agent_event…
- session_summary source_id must equal server_session_id
- Adapter rejected input
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/543b9e80d66fb088.
Report an issue: GitHub.
Appendix: source
Thrown at src/storage/postgres/generation-jobs.ts:270
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 d8bc9755e7)