hcengineering/platform · error

Action requires at least one document

Error message

Action requires at least one document

What it means

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).

Source

Thrown at plugins/questions-resources/src/actions/ActionWithAvailability.ts:75

      await action(evt as E, params)
    }
  }
}

export function anyActionWithAvailability<T extends Doc, E extends Event = Event, P = never> (
  isAvailable: (doc: T | T[]) => Promise<boolean>,
  action: (doc: T | T[], event?: E, params?: P) => Promise<any>
): ActionWithAvailability<T, P> {
  return {
    isAvailable: async function (doc: T | T[] | undefined): Promise<boolean> {
      if (doc === undefined) {
        return false
      }
      return await isAvailable(doc)
    },
    action: async function (doc: T | T[] | undefined, evt?: Event, params?: P): Promise<void> {
      if (doc === undefined) {
        throw new Error('Action requires at least one document')
      }
      if (!(await isAvailable(doc))) {
        throw new Error('Action not available')
      }
      await action(doc, evt as E, params)
    }
  }
}

/**
 * A special case of `any` action that performs an individual independent operation on each item,
 * and only if it is available for all items. Very opinionated about how to iterate items during
 * availability check and action invocation.
 *
 * If you're building an action different from that, consider using a general purpose
 * {@link anyActionWithAvailability()} and implementing your own iteration logic.
 *
 * @param isAvailable

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Check that the selection is defined and non-empty before invoking the action, and disable/hide the action UI otherwise
  2. Guard the call site: if (selection !== undefined) await action.action(selection, evt)
  3. Prefer using the action's isAvailable(doc) — it returns false for undefined — as a gate before calling action()
  4. Fix the wiring so the action is only triggered from UI contexts that guarantee a selection (e.g. row context menus, selection toolbar)

Example fix

// before
await deleteAction.action(selection, evt) // selection may be undefined

// after
if (selection === undefined) {
  ui.notify('Select at least one item first')
  return
}
await deleteAction.action(selection, evt)
Defensive patterns

Strategy: validation

Validate before calling

if (selection === undefined) {
  throw new SkipActionError('Select at least one document')
}
await actionObj.action(selection, evt)

Type guard

function isDefined<T>(value: T | undefined): value is T {
  return value !== undefined
}

Try / catch

try {
  await actionObj.action(selection, evt)
} catch (err) {
  if (err instanceof Error && err.message === 'Action requires at least one document') {
    ui.notify('Select at least one document first')
    return
  }
  throw err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/de21fd49e91507d5. Report an issue: GitHub.