{"record":{"id":"5956580624e81398","repo":"langfuse/langfuse","slug":"buildtemplateinusemessage-names","errorCode":null,"errorMessage":"buildTemplateInUseMessage(${names})","messagePattern":"buildTemplateInUseMessage\\((.+?)\\)","errorType":"exception","errorClass":"LangfuseConflictError","httpStatus":409,"severity":"error","filePath":"web/src/features/evals/server/legacyCompatibilityService.ts","lineNumber":1487,"sourceCode":"      const version = await tx.evaluatorVersion.findFirst({\n        where: { id: templateId, evaluator: { projectId } },\n        select: { evaluatorId: true },\n      });\n      if (!version) throw new LangfuseNotFoundError(\"Evaluator not found\");\n\n      // Lock the evaluator so a rule cannot be assigned to it between the\n      // usage check and the delete.\n      await tx.$executeRaw`SELECT \"id\" FROM \"evaluators\" WHERE \"id\" = ${version.evaluatorId} AND \"project_id\" = ${projectId} FOR UPDATE`;\n\n      const referencingRules = await tx.evaluationRule.findMany({\n        where: {\n          projectId,\n          assignments: { some: { evaluatorId: version.evaluatorId } },\n        },\n        select: { name: true },\n      });\n      if (referencingRules.length > 0) {\n        throw new LangfuseConflictError(\n          buildTemplateInUseMessage(referencingRules.map(({ name }) => name)),\n        );\n      }\n\n      const versions = await tx.evaluatorVersion.findMany({\n        where: { evaluatorId: version.evaluatorId },\n      });\n      await tx.evaluator.delete({\n        where: { id: version.evaluatorId, projectId },\n      });\n      return versions;\n    });\n  }\n}\n","sourceCodeStart":1469,"sourceCodeEnd":1502,"githubUrl":"https://github.com/langfuse/langfuse/blob/59d92c7cf365150d10b753b5a0d1708902a2ed60/web/src/features/evals/server/legacyCompatibilityService.ts#L1469-L1502","documentation":"Thrown as a LangfuseConflictError (409) when deleting an evaluator template that is still referenced by one or more evaluation rules. The message is built from the referencing rule names so users can see which rules block deletion. The service locks the evaluator row first, so the usage check is transactional.","triggerScenarios":"Calling deleteTemplate on a template while evaluation rules have assignments pointing to that template's evaluator; the referencingRules query inside the transaction returns one or more rule names.","commonSituations":"Trying to clean up old evaluator templates that are still attached to active or paused evaluation rules; bulk-deleting templates without first detaching them from rules.","solutions":["Remove or re-point the evaluation rules named in the error message first, then retry deletion","Use the rule update API to detach the evaluator assignment from each referencing rule","Parse the rule names from the 409 message to drive a UI prompt offering to detach them","If rules are unused, deactivate and delete them before deleting the template"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const referencing = await listRulesUsingTemplate(projectId, templateId);\nif (referencing.length === 0) {\n  await svc.deleteTemplate(projectId, templateId);\n}","typeGuard":"function isConflictError(e: unknown): e is { name: 'LangfuseConflictError'; message: string } {\n  return e instanceof Error && (e as any).name === 'LangfuseConflictError';\n}","tryCatchPattern":"try {\n  await svc.deleteTemplate(projectId, templateId);\n} catch (e) {\n  if (isConflictError(e)) {\n    // parse rule names from message, offer detach flow\n    const rules = parseRuleNames(e.message);\n    await detachEvaluatorFromRules(projectId, rules);\n    await svc.deleteTemplate(projectId, templateId);\n    return;\n  }\n  throw e;\n}","preventionTips":["Check rule assignments before showing a delete button for templates","Detach evaluators from rules as part of any template cleanup script"],"tags":["langfuse","evals","conflict","referential-integrity","delete"],"backgroundTag":"resource-in-use-conflict","analyzedSha":"59d92c7cf365150d10b753b5a0d1708902a2ed60","analyzedAt":"2026-08-27T22:22:00.402Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}