{"record":{"id":"0d8b0668c5bb7c85","repo":"grafana/grafana","slug":"annotation-name-already-exists","errorCode":null,"errorMessage":"Annotation '${name}' already exists","messagePattern":"Annotation '(.+?)' already exists","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"public/app/features/dashboard-scene/mutation-api/commands/addAnnotation.ts","lineNumber":42,"sourceCode":"  name: 'ADD_ANNOTATION',\n  description: payloads.addAnnotation.description ?? '',\n\n  payloadSchema: payloads.addAnnotation,\n  permission: requiresEdit,\n  readOnly: false,\n\n  handler: async (payload, context) => {\n    const { scene } = context;\n    enterEditModeIfNeeded(scene);\n\n    try {\n      // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Zod output is structurally compatible with AnnotationQueryKind\n      const annotation = payload.annotation as AnnotationQueryKind;\n      const name = annotation.spec.name;\n      const set = getAnnotationLayerSet(scene);\n\n      if (findAnnotationLayer(set, name)) {\n        throw new Error(`Annotation '${name}' already exists`);\n      }\n\n      if (annotation.spec.builtIn && hasBuiltInAnnotation(set)) {\n        throw new Error('Dashboard already has a built-in annotation layer');\n      }\n\n      const newLayer = buildAnnotationLayer(annotation);\n      const updated = [...set.state.annotationLayers];\n      const { position } = payload;\n\n      if (position !== undefined && position >= 0 && position < updated.length) {\n        updated.splice(position, 0, newLayer);\n      } else {\n        updated.push(newLayer);\n      }\n\n      replaceAnnotationLayers(set, updated);\n","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/grafana/grafana/blob/ae3104e3690e28e72c8f0c97efc12647a70f75c3/public/app/features/dashboard-scene/mutation-api/commands/addAnnotation.ts#L24-L60","documentation":"Thrown by the ADD_ANNOTATION dashboard-scene mutation command when a new annotation layer's spec.name collides with an existing annotation layer in the dashboard's annotation layer set. The command is wrapped in try/catch and converts the thrown Error into a { success:false, error } result, so callers see a failed mutation result rather than a propagated exception.","triggerScenarios":"Issuing ADD_ANNOTATION with payload.annotation.spec.name equal to the spec.name of an existing layer in getAnnotationLayerSet(scene).state.annotationLayers.","commonSituations":"User creates an annotation layer with a duplicate name; programmatic dashboards issuing multiple ADD_ANNOTATION commands with the same name; copy-paste of annotation specs; tests that re-use a name without clearing the set.","solutions":["Choose a unique annotation layer name (check findAnnotationLayer(set, name) first).","If overwriting was intended, use the update/replace command instead of ADD_ANNOTATION.","Surface the returned result.error in the UI when success === false.","In tests, use unique names per command."],"exampleFix":"// before\nconst res = await runMutation({ type: 'ADD_ANNOTATION', annotation });\n// after\nconst set = getAnnotationLayerSet(scene);\nif (findAnnotationLayer(set, annotation.spec.name)) {\n  notify.error(`Annotation '${annotation.spec.name}' already exists`);\n  return;\n}\nconst res = await runMutation({ type: 'ADD_ANNOTATION', annotation });","handlingStrategy":"validation","validationCode":"const set = getAnnotationLayerSet(scene);\nif (findAnnotationLayer(set, annotation.spec.name)) {\n  notify.error(`Annotation '${annotation.spec.name}' already exists`);\n  return;\n}","typeGuard":"const isUniqueAnnotationName = (set, name): boolean =>\n  !findAnnotationLayer(set, name);","tryCatchPattern":"const res = await runMutation({ type: 'ADD_ANNOTATION', annotation });\nif (!res.success) {\n  notify.error(res.error);\n}","preventionTips":["Check findAnnotationLayer before issuing ADD_ANNOTATION.","Use the update command for overwrite intent.","Always read result.success and surface result.error."],"tags":["dashboard-scene","annotations","mutation-api","validation"],"backgroundTag":null,"analyzedSha":"ae3104e3690e28e72c8f0c97efc12647a70f75c3","analyzedAt":"2026-08-12T12:48:38.752Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}