langgenius/dify · error · BaseError

usage_missing_arg

usage_missing_arg

Error message

--email is required

What it means

Raised by validate_node_id when the node_id path segment equals CONVERSATION_VARIABLE_NODE_ID or SYSTEM_VARIABLE_NODE_ID on the node-scoped variable endpoints (GET/DELETE /rag/pipelines/{pipeline_id}/workflows/draft/nodes/{node_id}/variables). The code deliberately blocks these two reserved node_ids here so that callers use the dedicated /system-variables and conversation-variable endpoints instead — an explicit Hyrum's-Law guard noted in the source comment. InvalidArgumentError maps to HTTP 400.

Source

Thrown at cli/src/commands/create/member/run.ts:41

  readonly io?: IOStreams
  readonly envLookup?: (k: string) => string | undefined
  readonly membersFactory?: (http: HttpClient) => MembersClient
}

export type CreateMemberResult = {
  readonly data: InviteOutput
  readonly workspaceId: string
}

// `owner` is intentionally absent — ownership transfer is console-only.
const ASSIGNABLE_ROLES = new Set(['normal', 'admin'])

export async function runCreateMember(
  opts: CreateMemberOptions,
  deps: CreateMemberDeps,
): Promise<CreateMemberResult> {
  if (opts.email === undefined || opts.email === '') {
    throw new BaseError({
      code: ErrorCode.UsageMissingArg,
      message: '--email is required',
    })
  }
  if (!ASSIGNABLE_ROLES.has(opts.role)) {
    throw new BaseError({
      code: ErrorCode.UsageInvalidFlag,
      message: `invalid --role "${opts.role}"`,
      hint: 'expected: normal | admin (ownership transfer is console-only)',
    })
  }

  const env = deps.envLookup ?? ((k: string) => process.env[k])
  const factory = deps.membersFactory ?? ((h: HttpClient) => new MembersClient(h))
  const io = deps.io ?? nullStreams()
  const cs = colorScheme(colorEnabled(io.isErrTTY))

  const wsId = resolveWorkspaceId({

View on GitHub (pinned to ef8544b173)

Solutions

  1. For system variables, call GET /rag/pipelines/{pipeline_id}/workflows/draft/system-variables instead.
  2. For conversation variables, use the dedicated conversation-variable endpoint exposed under the same pipeline route group.
  3. Filter the reserved node_ids (CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID) out of any generic node-variable request before sending.

Example fix

// before
await get(`/rag/pipelines/${pipelineId}/workflows/draft/nodes/${nodeId}/variables`);  // nodeId === 'conversation'
// after
if (nodeId === CONVERSATION_VARIABLE_NODE_ID) {
  await get(`/rag/pipelines/${pipelineId}/workflows/draft/conversation-variables`);
} else if (nodeId === SYSTEM_VARIABLE_NODE_ID) {
  await get(`/rag/pipelines/${pipelineId}/workflows/draft/system-variables`);
} else {
  await get(`/rag/pipelines/${pipelineId}/workflows/draft/nodes/${nodeId}/variables`);
}
Defensive patterns

Strategy: type-guard

Validate before calling

const RESERVED_NODE_IDS = new Set([CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID]);
function isGenericNodeId(nodeId: string): boolean {
  return !RESERVED_NODE_IDS.has(nodeId);
}
if (!isGenericNodeId(nodeId)) {
  throw new Error(`Use the dedicated endpoint for node_id=${nodeId}`);
}

Type guard

const RESERVED = new Set([CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID]);
function isReservedNodeId(nodeId: string): boolean {
  return RESERVED.has(nodeId);
}

Try / catch

try {
  await client.get(`/rag/pipelines/${pipelineId}/workflows/draft/nodes/${nodeId}/variables`);
} catch (e) {
  if (e.response?.status === 400 && /invalid node_id/i.test(e.response.data?.message || '')) {
    // route to the dedicated endpoint
    if (nodeId === SYSTEM_VARIABLE_NODE_ID) { await getSystemVariables(pipelineId); }
    else { await getConversationVariables(pipelineId); }
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: GET /console/api/rag/pipelines/{id}/workflows/draft/nodes/conversation/variables or .../nodes/system/variables (or whatever the exact reserved constants resolve to). Any client that learned the internal node_id storage layout and tried to read system/conversation variables through the generic node endpoint.

Common situations: A client ported from an older API version that allowed these node_ids; reverse-engineered integrations that hit the generic node endpoint with the reserved ids; copy-paste of a stored node_id without filtering the reserved ones.

Related errors


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