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
- Pass `--action <action-id>` using one of the IDs from the form's `user_actions` array.
- Inspect the form first: `difyctl describe` or GET `apps/{appId}/human-input-forms/{formToken}` to list valid action IDs.
- 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
- For forms with multiple buttons, always pass `--action <id>`.
- Inspect the form's `user_actions` before resuming to know valid action IDs.
- Design workflow forms with a single action when auto-selection is desired.
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.