{"record":{"id":"de21fd49e91507d5","repo":"hcengineering/platform","slug":"action-requires-at-least-one-document","errorCode":null,"errorMessage":"Action requires at least one document","messagePattern":"Action requires at least one document","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/questions-resources/src/actions/ActionWithAvailability.ts","lineNumber":75,"sourceCode":"      await action(evt as E, params)\n    }\n  }\n}\n\nexport function anyActionWithAvailability<T extends Doc, E extends Event = Event, P = never> (\n  isAvailable: (doc: T | T[]) => Promise<boolean>,\n  action: (doc: T | T[], event?: E, params?: P) => Promise<any>\n): ActionWithAvailability<T, P> {\n  return {\n    isAvailable: async function (doc: T | T[] | undefined): Promise<boolean> {\n      if (doc === undefined) {\n        return false\n      }\n      return await isAvailable(doc)\n    },\n    action: async function (doc: T | T[] | undefined, evt?: Event, params?: P): Promise<void> {\n      if (doc === undefined) {\n        throw new Error('Action requires at least one document')\n      }\n      if (!(await isAvailable(doc))) {\n        throw new Error('Action not available')\n      }\n      await action(doc, evt as E, params)\n    }\n  }\n}\n\n/**\n * A special case of `any` action that performs an individual independent operation on each item,\n * and only if it is available for all items. Very opinionated about how to iterate items during\n * availability check and action invocation.\n *\n * If you're building an action different from that, consider using a general purpose\n * {@link anyActionWithAvailability()} and implementing your own iteration logic.\n *\n * @param isAvailable","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/plugins/questions-resources/src/actions/ActionWithAvailability.ts#L57-L93","documentation":"anyActionWithAvailability wraps an action that accepts one or many documents. Its action() throws Error('Action requires at least one document') when invoked with doc === undefined, i.e. no document/selection was passed at all. The 'any' variant requires a non-undefined input (unlike focus which demands exactly one, or eachItem which also rejects empty arrays).","triggerScenarios":"Calling action() on an action created by anyActionWithAvailability and passing undefined as the doc argument — typically when the UI selection is empty and the caller forwards it unguarded to the action.","commonSituations":"A bulk action (e.g. archive/delete selected items) invoked with nothing selected; a keyboard shortcut or toolbar button wired to the action without disabling it for empty selections; custom code reading a selection from state that defaults to undefined before any item is picked.","solutions":["Check that the selection is defined and non-empty before invoking the action, and disable/hide the action UI otherwise","Guard the call site: if (selection !== undefined) await action.action(selection, evt)","Prefer using the action's isAvailable(doc) — it returns false for undefined — as a gate before calling action()","Fix the wiring so the action is only triggered from UI contexts that guarantee a selection (e.g. row context menus, selection toolbar)"],"exampleFix":"// before\nawait deleteAction.action(selection, evt) // selection may be undefined\n\n// after\nif (selection === undefined) {\n  ui.notify('Select at least one item first')\n  return\n}\nawait deleteAction.action(selection, evt)","handlingStrategy":"validation","validationCode":"if (selection === undefined) {\n  throw new SkipActionError('Select at least one document')\n}\nawait actionObj.action(selection, evt)","typeGuard":"function isDefined<T>(value: T | undefined): value is T {\n  return value !== undefined\n}","tryCatchPattern":"try {\n  await actionObj.action(selection, evt)\n} catch (err) {\n  if (err instanceof Error && err.message === 'Action requires at least one document') {\n    ui.notify('Select at least one document first')\n    return\n  }\n  throw err\n}","preventionTips":["Never pass selection state straight through without an undefined check","Disable toolbar/shortcut actions when the selection is empty","Favor UI entry points (context menus, selection toolbars) that guarantee a selection","Use isAvailable(doc) as a cheap gate — it returns false for undefined"],"tags":["missing-argument","action-availability","selection"],"backgroundTag":"missing-required-document","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}