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
- For system variables, call GET /rag/pipelines/{pipeline_id}/workflows/draft/system-variables instead.
- For conversation variables, use the dedicated conversation-variable endpoint exposed under the same pipeline route group.
- 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
- Filter the reserved node_ids before calling the generic node-variables endpoint.
- Use /system-variables and the conversation-variable endpoints for those scopes.
- Keep the reserved constants in sync with the backend definitions.
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.