{"record":{"id":"232392a50332ea65","repo":"langgenius/dify","slug":"usage-missing-arg-232392","errorCode":"usage_missing_arg","errorMessage":"--email is required","messagePattern":"--email is required","errorType":"error_code","errorClass":"BaseError","httpStatus":null,"severity":"error","filePath":"cli/src/commands/create/member/run.ts","lineNumber":41,"sourceCode":"  readonly io?: IOStreams\n  readonly envLookup?: (k: string) => string | undefined\n  readonly membersFactory?: (http: HttpClient) => MembersClient\n}\n\nexport type CreateMemberResult = {\n  readonly data: InviteOutput\n  readonly workspaceId: string\n}\n\n// `owner` is intentionally absent — ownership transfer is console-only.\nconst ASSIGNABLE_ROLES = new Set(['normal', 'admin'])\n\nexport async function runCreateMember(\n  opts: CreateMemberOptions,\n  deps: CreateMemberDeps,\n): Promise<CreateMemberResult> {\n  if (opts.email === undefined || opts.email === '') {\n    throw new BaseError({\n      code: ErrorCode.UsageMissingArg,\n      message: '--email is required',\n    })\n  }\n  if (!ASSIGNABLE_ROLES.has(opts.role)) {\n    throw new BaseError({\n      code: ErrorCode.UsageInvalidFlag,\n      message: `invalid --role \"${opts.role}\"`,\n      hint: 'expected: normal | admin (ownership transfer is console-only)',\n    })\n  }\n\n  const env = deps.envLookup ?? ((k: string) => process.env[k])\n  const factory = deps.membersFactory ?? ((h: HttpClient) => new MembersClient(h))\n  const io = deps.io ?? nullStreams()\n  const cs = colorScheme(colorEnabled(io.isErrTTY))\n\n  const wsId = resolveWorkspaceId({","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/cli/src/commands/create/member/run.ts#L23-L59","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait get(`/rag/pipelines/${pipelineId}/workflows/draft/nodes/${nodeId}/variables`);  // nodeId === 'conversation'\n// after\nif (nodeId === CONVERSATION_VARIABLE_NODE_ID) {\n  await get(`/rag/pipelines/${pipelineId}/workflows/draft/conversation-variables`);\n} else if (nodeId === SYSTEM_VARIABLE_NODE_ID) {\n  await get(`/rag/pipelines/${pipelineId}/workflows/draft/system-variables`);\n} else {\n  await get(`/rag/pipelines/${pipelineId}/workflows/draft/nodes/${nodeId}/variables`);\n}","handlingStrategy":"type-guard","validationCode":"const RESERVED_NODE_IDS = new Set([CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID]);\nfunction isGenericNodeId(nodeId: string): boolean {\n  return !RESERVED_NODE_IDS.has(nodeId);\n}\nif (!isGenericNodeId(nodeId)) {\n  throw new Error(`Use the dedicated endpoint for node_id=${nodeId}`);\n}","typeGuard":"const RESERVED = new Set([CONVERSATION_VARIABLE_NODE_ID, SYSTEM_VARIABLE_NODE_ID]);\nfunction isReservedNodeId(nodeId: string): boolean {\n  return RESERVED.has(nodeId);\n}","tryCatchPattern":"try {\n  await client.get(`/rag/pipelines/${pipelineId}/workflows/draft/nodes/${nodeId}/variables`);\n} catch (e) {\n  if (e.response?.status === 400 && /invalid node_id/i.test(e.response.data?.message || '')) {\n    // route to the dedicated endpoint\n    if (nodeId === SYSTEM_VARIABLE_NODE_ID) { await getSystemVariables(pipelineId); }\n    else { await getConversationVariables(pipelineId); }\n    return;\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["rag-pipeline","workflow","variables","validation","reserved-id","rest-api","console"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}