{"record":{"id":"3957062af3f07ff2","repo":"amir20/dozzle","slug":"toast-id-is-required-when-once-is-true","errorCode":null,"errorMessage":"Toast id is required when once is true","messagePattern":"Toast id is required when once is true","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"assets/composable/toast.ts","lineNumber":40,"sourceCode":"type ToastOptions = {\n  expire?: number;\n  once?: boolean;\n  timed?: number;\n};\n\nconst toasts = ref<\n  {\n    toast: Toast;\n    options: ToastOptions;\n  }[]\n>([]);\n\nconst showToast = (\n  toast: Omit<Toast, \"id\" | \"createdAt\"> & { id?: string },\n  { expire = -1, once = false, timed }: ToastOptions = { expire: -1, once: false },\n) => {\n  if (once && !toast.id) {\n    throw new Error(\"Toast id is required when once is true\");\n  }\n  if (once && toasts.value.some((t) => t.toast.id === toast.id)) {\n    return;\n  }\n\n  const toastWithId = {\n    id: Date.now().toString(),\n    ...toast,\n    createdAt: new Date(),\n  };\n  toasts.value.push({\n    toast: toastWithId,\n    options: { expire, once, timed },\n  });\n\n  if (expire > 0) {\n    setTimeout(() => {\n      removeToast(toastWithId.id);","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/assets/composable/toast.ts#L22-L58","documentation":"showToast() accepts an optional toast id; when the once option is true the id becomes mandatory, because de-duplication against existing toasts is keyed by id. If once is set without an id the function throws immediately instead of de-duplicating by undefined, which would collapse all once-toasts into one.","triggerScenarios":"Calling showToast({ title: \"...\" }, { once: true }) with no id property on the toast object. Any caller that enables once but constructs the toast object dynamically and omits id (or passes id: undefined) triggers the throw on every call.","commonSituations":"Adding once: true to an existing showToast call as a quick dedup fix without adding an id; generating toasts from a loop/notify helper that drops optional fields; TypeScript not catching it because id is optional in the parameter type.","solutions":["Pass a stable id on the toast object whenever using once: true, e.g. showToast({ id: \"conn-lost\", title: \"Connection lost\" }, { once: true }).","Make the id meaningful and stable per logical event (e.g. container id + event kind) so dedup works as intended.","If dedup is not actually needed, remove once: true instead of adding an id.","Centralize toast creation in a helper that enforces id presence when once is requested."],"exampleFix":"// before\nshowToast({ title: \"Container stopped\" }, { once: true }); // throws\n// after\nshowToast({ id: \"container-stopped-\" + container.id, title: \"Container stopped\" }, { once: true });","handlingStrategy":"validation","validationCode":"// before calling showToast with once\nfunction showOnce(toast: { id?: string } & Record<string, unknown>, opts?: ToastOptions) {\n  if (opts?.once && !toast.id) throw new Error(\"once requires id\");\n  showToast(toast, opts);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Adopt a convention of always passing an id to showToast, then once is always safe.","Derive ids deterministically (event kind + entity id) so dedup semantics are predictable.","Wrap toast creation in a helper that strips once when id is missing.","Review showToast call sites when adding once: true retroactively."],"tags":["api","validation","developer-error","toast"],"backgroundTag":"missing-required-argument","analyzedSha":"d9463cbe21874e44ab79db6fa63e746ca7d22928","analyzedAt":"2026-09-07T10:08:55.855Z","contentChangedAt":"2026-09-07T10:08:55.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}