Skip to content

Job Lifecycle & States

Every evaluation job (POST /api/v1/evaluations/jobs) runs one or more benchmarks. EvalHub tracks state at two levels:

  • Benchmark state — the status of each individual benchmark within the job.
  • Overall job state — a single aggregate value derived from the states of all benchmarks.

The overall state is not set directly; it is recomputed every time a benchmark reports a status update.

Each benchmark within a job has its own state. Adapters report state changes by posting status events to the server via the SDK callbacks interface.

StateTerminalDescription
pendingNoInitial state; benchmark has not started
runningNoAdapter is processing the benchmark
completedYesBenchmark finished successfully
failedYesBenchmark encountered an error
cancelledYesSet when the job is cancelled; adapters cannot report this state via events

Once a benchmark reaches a terminal state, subsequent non-terminal status events for that benchmark are rejected.

Within the running state, adapters can optionally report a finer-grained phase to indicate progress. Phases are informational and do not affect the overall job state.

PhaseDescription
initializingAdapter is setting up
loading_dataLoading evaluation data
running_evaluationExecuting the benchmark
post_processingProcessing results
persisting_artifactsSaving outputs (e.g. to MLflow)
completedBenchmark processing finished

The overall state is recomputed from the combination of all benchmark states every time a benchmark status update is received.

StateTerminalDescription
pendingNoNo benchmarks have reported progress yet
runningNoAt least one benchmark has reported a state
completedYesAll benchmarks completed successfully
failedYesAll benchmarks failed
partially_failedYesAll benchmarks are terminal, but with a mix of completed and failed
cancelledYesJob was cancelled by the user

The overall state is determined by counting benchmark outcomes. Given total benchmarks:

ConditionOverall state
All completedcompleted
All failedfailed
All completed or failed (mixed)partially_failed
At least one has reported a staterunning
No benchmarks have reported yetpending

Once the overall job state reaches a terminal value (completed, failed, partially_failed, or cancelled), no further status updates are accepted. The API returns an error if an adapter attempts to update a benchmark on a terminal job.

Cancel a job by sending DELETE /api/v1/evaluations/jobs/{id}. This:

  1. Stops the runtime resources (Kubernetes Jobs or local processes).
  2. Sets all non-terminal benchmarks to cancelled.
  3. Sets the overall state to cancelled.

To permanently remove a job record from storage, use the hard_delete query parameter:

DELETE /api/v1/evaluations/jobs/{id}?hard_delete=true

Unlike cancellation, this does not set any state — it deletes the job entirely. The job will no longer appear in list or get responses.

EvalHub does not automatically retry failed benchmarks or jobs. On Kubernetes, backing Jobs are created with backoffLimit: 0 and pods use restartPolicy: Never. To re-run a failed evaluation, submit a new job.

Terminal window
# Get a specific job (includes status and results)
curl -s "$EVALHUB_URL/api/v1/evaluations/jobs/$JOB_ID" | jq .
Terminal window
# List jobs filtered by status
curl -s "$EVALHUB_URL/api/v1/evaluations/jobs?status=failed" | jq .

The status field in the job response contains the overall state and per-benchmark details:

{
"status": {
"state": "running",
"message": {
"message": "Evaluation job is running",
"message_code": "evaluation_job_updated",
"message_origin": "server"
},
"benchmarks": [
{
"provider_id": "lighteval",
"id": "hellaswag",
"benchmark_index": 0,
"status": "completed",
"phase": "completed",
"started_at": "2026-08-17T12:00:00Z",
"completed_at": "2026-08-17T12:05:30Z"
},
{
"provider_id": "lighteval",
"id": "mmlu",
"benchmark_index": 1,
"status": "running",
"phase": "running_evaluation",
"started_at": "2026-08-17T12:00:00Z"
}
]
}
}

On Kubernetes, EvalHub propagates lifecycle state through native Kubernetes primitives (labels, events, and annotations) on the backing Job for each benchmark. This enables cluster automation without polling the EvalHub API.

For details, see Evaluation Lifecycle Signals and Evaluation Event Violations.