Evaluator Configuration and Status Management#
Langfuse evaluators (LLM-as-a-Judge jobs) have two distinct status mechanisms:
- Blocking — automatic pausing when LLM/model configuration is missing or broken
- Deactivation — manual ACTIVE/INACTIVE toggle with server-side guards that prevent certain changes once an evaluator has run on existing traces
Config Blocking#
Evaluators are automatically blocked when the worker detects a configuration problem during execution. The isJobConfigExecutable function gates all eval runs — a config must have status === ACTIVE and blockedAt === null to be executable. If blocked, the job execution is cancelled immediately.
Blocking is triggered from four sources :
EvaluatorBlockSource | When |
|---|---|
INVALID_MODEL_CONFIG | Model config invalid during eval execution |
LLM_COMPLETION_ERROR | Auth failure (401) or model unavailable (404) from LLM call |
LLM_API_KEY_DELETION | An LLM connection used by the evaluator is deleted |
DEFAULT_EVAL_MODEL_DELETION | The project's default evaluation model is deleted |
Block Reasons & Messages#
Block reason, short label, and user-facing message are defined in evalConfigBlocking.ts:
EvaluatorBlockReason | Short Label |
|---|---|
LLM_CONNECTION_AUTH_INVALID | Authentication failed |
LLM_CONNECTION_MISSING | LLM connection missing |
DEFAULT_EVAL_MODEL_MISSING | Default evaluation model missing |
EVAL_MODEL_CONFIG_INVALID | Evaluation model invalid |
EVAL_MODEL_UNAVAILABLE | Model unavailable |
PROVIDER_ACCOUNT_NOT_READY | Provider account setup incomplete |
Database Fields#
Three nullable columns on JobConfiguration track blocked state :
blockedAt— timestamp when the block was setblockReason—EvaluatorBlockReasonenum valueblockMessage— human-readable remediation text
Notifications & Side Effects#
When blockEvaluatorConfigs is called, it:
- Updates the DB record with
blockedAt,blockReason, andblockMessage - Sends an email to project admins via
notifyBlockedEvaluatorConfigs - Invalidates project eval config caches so workers stop picking up the config immediately
Frontend: Blocked State UI#
The EvaluatorPausedCallout component renders a yellow alert banner when evalConfig.blockedAt is set. It surfaces:
- The
blockMetadata.shortLabeland elapsed time since blocked - The full
blockMessagefromgetEvaluatorBlockMetadata - A context-aware "fix it" link via
getEvaluatorBlockResolutionPath: LLM auth/missing issues resolve to/settings/llm-connections; other issues link to the evaluator template or general evals page - A Reactivate button (calls
updateEvalJobwithstatus: ACTIVE), withonSuccessandonErrortoast notifications
Reactivation & Block Field Reset#
When updateEvalJob receives a status update, the router automatically clears block fields by merging resetEvalConfigBlockFields into the update payload . Before activating a previously-blocked config, the router calls validateEvalTemplateCanRun to confirm the underlying issue is resolved .
Server-Side Guards on updateEvalJob#
Beyond blocking, updateEvalJob enforces two immutability rules for evaluators that have already run on existing traces :
-
timeScopeimmutability — OnceEXISTINGis intimeScope, it cannot be removed. ThrowsBAD_REQUEST: "The evaluator ran on existing traces already. This cannot be changed anymore." -
EXISTING-only deactivation guard — For legacy
TRACE/DATASETtargets (notEVENT), iftimeScopeis["EXISTING"]only, the evaluator cannot be set toINACTIVE. ThrowsBAD_REQUEST: "The evaluator is running on existing traces only and cannot be deactivated."
The DeactivateEvalConfig switch pre-disables itself when timeScope === ["EXISTING"] , preventing the second guard from being triggered via the toggle UI.
Frontend Error Handling: Silent Failure & Fix#
Bug (issue #13315): DeactivateEvalConfig had no onError handler on the updateEvalJob mutation. When the server rejected a request (e.g., EXISTING-scope guard), the error was silently swallowed — the form reverted with no toast or inline message .
Fix (PR #14064): Added showErrorToast in the onError callback, consistent with the pattern in evaluator-paused-callout.tsx and inner-evaluator-form.tsx .
Known residual issue: The onClick handler still calls mutateAsync without await or .catch() , leaving a dangling unhandled promise rejection on failure. The recommended fix is switching to mutate (fire-and-forget), as evaluator-paused-callout.tsx does .
Key Files#
| File | Purpose |
|---|---|
packages/shared/src/features/evals/evalConfigBlocking.ts | Block reason enum, metadata, resolution paths, isJobConfigExecutable |
packages/shared/src/server/services/blockEvaluatorConfigs.ts | Service: write block fields, notify admins, invalidate caches |
web/src/features/evals/server/router.ts | updateEvalJob mutation with server-side guards (lines 1084–1200+) |
web/src/features/evals/components/evaluator-paused-callout.tsx | UI alert for blocked evaluators with reactivation button |
web/src/features/evals/components/deactivate-config.tsx | Active/Inactive toggle switch |
worker/src/features/evaluation/evalService.ts | Triggers blocking on LLM errors during eval execution |