langgenius/dify · error · Error

--action required: form has multiple user actions

Error message

--action required: form has multiple user actions

What it means

Thrown by `resume app` (cli/src/commands/resume/app/run.ts:60) as a plain `Error` (no BaseError/code) when `--action` is omitted and the fetched human-input form at `apps/{appId}/human-input-forms/{formToken}` returns more than one `user_actions` entry. The CLI cannot disambiguate which action to submit, so it aborts before calling `submitHumanInput`. Note: unlike most errors here it lacks a `code`, so it surfaces as a generic exit-1.

Source

Thrown at cli/src/commands/resume/app/run.ts:60

  const apps = selectAppReader(deps.active, deps.http)
  const meta = new AppMetaClient({ apps, host: deps.host, cache: deps.cache })
  const m = await meta.get(opts.appId, [FieldInfo])
  const mode = m.info?.mode ?? RUN_MODES.Workflow

  const runClient = new AppRunClient(deps.http)
  const exit = deps.exit ?? processExit

  let action = opts.action
  if (action === undefined) {
    const formResp = await deps.http.get<{ user_actions: { id: string }[] }>(
      `apps/${encodeURIComponent(opts.appId)}/human-input-forms/${encodeURIComponent(opts.formToken)}`,
    )
    if (formResp.user_actions.length === 1) {
      action = formResp.user_actions[0]?.id ?? ''
    } else if (formResp.user_actions.length === 0) {
      action = ''
    } else {
      throw new Error('--action required: form has multiple user actions')
    }
  }

  const inputs = await resolveInputs(opts.inputsJson, opts.inputsFile, opts.inputs)
  await runClient.submitHumanInput(opts.appId, opts.formToken, action, inputs)

  const format = opts.format ?? ''
  const isText = TEXT_FORMATS.has(format)

  if (isText) {
    const cs = colorScheme(colorEnabled(deps.io.isErrTTY))
    deps.io.err.write(`${cs.successIcon()} ${cs.bold('form submitted')}\n`)
    deps.io.err.write(`  ${cs.dim('workflow execution resumed')}\n`)
  }
  const livePrint = opts.stream === true

  const adaptedRunClient = {
    runStream: (_appId: string, _body: unknown, streamOpts?: { signal?: AbortSignal }) =>

View on GitHub (pinned to ef8544b173)

Solutions

  1. Pass `--action <action-id>` using one of the IDs from the form's `user_actions` array.
  2. Inspect the form first: `difyctl describe` or GET `apps/{appId}/human-input-forms/{formToken}` to list valid action IDs.
  3. If only one action is intended, update the workflow form to expose a single user action so auto-selection applies.

Example fix

// before
$ difyctl resume app --app <uuid> --form-token <tok>
// after
$ difyctl resume app --app <uuid> --form-token <tok> --action approve
Defensive patterns

Strategy: validation

Validate before calling

// Before calling resumeApp, fetch the form and pass --action explicitly.
const form = await http.get<{ user_actions: { id: string }[] }>(
  `apps/${appId}/human-input-forms/${formToken}`,
)
if (form.user_actions.length !== 1 && !opts.action) {
  throw new Error(`pass --action: ${form.user_actions.map(a => a.id).join(', ')}`)
}

Try / catch

try {
  await resumeApp(opts, deps)
} catch (err) {
  if (err instanceof Error && /--action required/.test(err.message)) {
    // list actions from the form, then retry with opts.action set
  }
  throw err
}

Prevention

When it happens

Trigger: Calling `difyctl resume app` without `--action` on a form that offers multiple buttons (e.g. Approve + Reject, or Submit + Cancel). The form fetch returns `user_actions.length >= 2`, hitting the `else` branch at line 59-61.

Common situations: A workflow with a human-in-the-loop approval node that branches on multiple outcomes; the form was designed with several action buttons; the caller assumed auto-selection (single action) but the app author added a second option.

Related errors


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