paperclipai/paperclip · error
schedule references unknown candidate ${entry.candidateId}
Error message
schedule references unknown candidate ${entry.candidateId} What it means
Thrown by executeRunnerLiveSchedule when a schedule entry's candidateId does not match any candidate in schedule.candidates. The function builds the candidate lookup inside the execution loop and fails fast rather than silently skipping malformed schedule entries. It indicates the schedule was constructed with inconsistent entry/candidate data.
Source
Thrown at packages/paperclip-runner/src/eval/live-workflow-matrix.ts:488
export async function executeRunnerLiveSchedule(
schedule: RunnerLiveEvalSchedule,
execute: (
entry: RunnerLiveScheduleEntry,
candidate: RunnerLiveEvalCandidate,
) => Promise<RunnerWorkflowObservation>,
onInfrastructureFailure?: (
entry: RunnerLiveScheduleEntry,
candidate: RunnerLiveEvalCandidate,
error: RunnerWorkflowInfrastructureError,
) => RunnerWorkflowObservation | Promise<RunnerWorkflowObservation>,
): Promise<RunnerWorkflowObservation[]> {
const results: RunnerWorkflowObservation[] = [];
for (const entry of schedule.entries) {
const resolved = schedule.candidates.find(
(candidate) => candidate.id === entry.candidateId,
);
if (!resolved)
throw new Error(
`schedule references unknown candidate ${entry.candidateId}`,
);
let infrastructureAttempts = 0;
while (true) {
try {
results.push(await execute(entry, resolved));
break;
} catch (error) {
if (!(error instanceof RunnerWorkflowInfrastructureError)) {
throw error;
}
if (!error.retryable || infrastructureAttempts >= 1) {
if (onInfrastructureFailure === undefined) throw error;
results.push(await onInfrastructureFailure(entry, resolved, error));
break;
}
infrastructureAttempts += 1;
}View on GitHub (pinned to 01ad858492)
Solutions
- Inspect schedule.entries and schedule.candidates; make every entry.candidateId exist in candidates before calling executeRunnerLiveSchedule
- Fix the schedule builder so entries are generated from the same candidate list it embeds (derive entry.candidateId from candidates[i].id)
- If filtering candidates, filter entries with the same predicate
- Add a pre-call validation that every entry.candidateId is in the candidate id set
Example fix
// before
const schedule = { candidates: candidates.filter(c => c.enabled), entries: allEntries };
await executeRunnerLiveSchedule(schedule);
// after
const enabled = candidates.filter(c => c.enabled);
const schedule = { candidates: enabled, entries: allEntries.filter(e => enabled.some(c => c.id === e.candidateId)) };
await executeRunnerLiveSchedule(schedule); Defensive patterns
Strategy: validation
Validate before calling
const ids = new Set(schedule.candidates.map(c => c.id));
const bad = schedule.entries.filter(e => !ids.has(e.candidateId));
if (bad.length) throw new Error(`entries reference unknown candidates: ${bad.map(e => e.candidateId).join(', ')}`); Type guard
function isScheduleValid(schedule: { candidates: {id: string}[]; entries: {candidateId: string}[] }): boolean {
const ids = new Set(schedule.candidates.map(c => c.id));
return schedule.entries.every(e => ids.has(e.candidateId));
} Try / catch
try {
await executeRunnerLiveSchedule(schedule);
} catch (err) {
if (err instanceof Error && err.message.startsWith('schedule references unknown candidate')) {
// log the schedule and skip/rebuild
} else throw err;
} Prevention
- Build entries and candidates from a single source of truth
- Validate schedule integrity before passing it to the runner
- Filter candidates and entries with the same predicate
When it happens
Trigger: Calling executeRunnerLiveSchedule with a LiveWorkflowSchedule where some entry.candidateId was never added to schedule.candidates (typo, filtered-out candidate, or hand-built schedule).
Common situations: Hand-assembled schedules in eval scripts; candidates deduplicated or filtered after entries were generated; candidate IDs regenerated (e.g. UUIDs) after entries referenced old IDs.
Related errors
- live schedule coverage failed: ${JSON.stringify(coverage)}
- provider turn ended with status ${turn.status}
- live_provider_execution_failed
- PAPERCLIP_EVAL_MAX_CAMPAIGN_COST_USD must be a positive fini
- Runner live rotation requires a valid generated-at time
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-02).
Data as JSON: /api/errors/9086e4f3ab6a09bc.
Report an issue: GitHub.