langgenius/dify · error · BaseError

UsageMissingArg

UsageMissingArg

Error message

nothing to select

What it means

BaseError(UsageMissingArg, exit 2) thrown at the top of selectFromList (select.ts:25) when opts.items is empty. This is the picker's own defensive guard, independent of any specific caller. Most callers (e.g. pickWorkspaceId in use.ts) pre-check and throw their own contextual error, so this fires for callers that pass items without pre-validation — typically a programming error in a command, not a user mistake.

Source

Thrown at cli/src/sys/io/select.ts:25

export type SelectOptions<T> = {
  readonly io: IOStreams
  readonly items: readonly T[]
  readonly header: string
  /** Single rich line shown per option. */
  readonly render: (item: T) => string
  /** Optional second line shown only for the focused option in the TTY picker. */
  readonly describe?: (item: T) => string
}

const HIDE_CURSOR = '\x1B[?25l'
const SHOW_CURSOR = '\x1B[?25h'
const CLEAR_DOWN = '\x1B[0J'
const cursorUp = (n: number): string => `\x1B[${n}A`

export async function selectFromList<T>(opts: SelectOptions<T>): Promise<T> {
  if (opts.items.length === 0)
    throw new BaseError({ code: ErrorCode.UsageMissingArg, message: 'nothing to select' })
  return opts.io.isErrTTY ? pickInteractive(opts) : pickNumbered(opts)
}

/**
 * Arrow-key picker built on Node's readline keypress events — no third-party
 * prompt library, so it bundles cleanly into the compiled binary. Renders to
 * the err stream, redrawing in place on each keystroke and erasing itself on
 * exit so the caller's own output starts on a clean row.
 */
async function pickInteractive<T>(opts: SelectOptions<T>): Promise<T> {
  const input = opts.io.in as NodeJS.ReadStream
  const out = opts.io.err
  const cs = colorScheme(colorEnabled(opts.io.isErrTTY))
  const count = opts.items.length

  return new Promise<T>((resolve, reject) => {
    let active = 0
    let rendered = 0

View on GitHub (pinned to ef8544b173)

Solutions

  1. If you're a user: this usually means the underlying list is empty — run the corresponding `list` subcommand to verify, then create/invite/import to populate it.
  2. If you're calling selectFromList in code: check `items.length` first and throw a domain-specific BaseError with context (as pickWorkspaceId does for workspaces).
  3. Provide an explicit argument (e.g. an id) to skip the picker entirely.
  4. File a bug if a built-in command surfaces this raw message — it should be caught upstream with a friendlier error.

Example fix

// before — caller passes items straight to the picker
const picked = await selectFromList({ io, items: devices, header: 'Select', render })

// after — guard explicitly, mirror pickWorkspaceId's pattern
if (devices.length === 0) {
  throw new BaseError({ code: ErrorCode.AccessDenied, message: 'no devices available' })
}
const picked = await selectFromList({ io, items: devices, header: 'Select', render })
Defensive patterns

Strategy: validation

Validate before calling

// guard before calling selectFromList
function assertNonEmpty<T>(items: readonly T[], what: string): void {
  if (items.length === 0) {
    throw new Error(`no ${what} available to select`)
  }
}
// usage: assertNonEmpty(devices, 'devices')

Type guard

function hasItems<T>(items: readonly T[]): items is readonly [T, ...T[]] {
  return items.length > 0
}

Prevention

When it happens

Trigger: A command calls selectFromList with a list that resolved to zero entries without first checking length. E.g. listing devices, workspaces, or apps and immediately opening a picker on the result. If the upstream list call returned [], the picker rejects it here rather than rendering an empty menu.

Common situations: Command author forgot the empty-state guard; an API returned an unexpected empty payload; a filter removed all items; testing a new picker-based command with empty fixtures.

Related errors


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/3618b153b0660420. Report an issue: GitHub.