{"record":{"id":"b7e58f4657fea10c","repo":"nanocoai/nanoclaw","slug":"user-is-required-b7e58f","errorCode":null,"errorMessage":"--user is required","messagePattern":"--user is required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/resources/roles.ts","lineNumber":38,"sourceCode":"      name: 'agent_group_id',\n      type: 'string',\n      description:\n        'Null = global (all groups). A specific ID limits the role to that group. Owner must always be null.',\n    },\n    { name: 'granted_by', type: 'string', description: 'Who granted this role. Informational.' },\n    { name: 'granted_at', type: 'string', description: 'Auto-set.' },\n  ],\n  operations: { list: 'open' },\n  customOperations: {\n    grant: {\n      access: 'approval',\n      description: 'Grant a role. Use --user, --role, and optionally --group for scoped admin.',\n      handler: async (args) => {\n        const userId = args.user as string;\n        const role = args.role as string;\n        const groupId = (args.group as string) ?? null;\n        const grantedBy = (args.granted_by as string) ?? null;\n        if (!userId) throw new Error('--user is required');\n        if (!role || !['owner', 'admin'].includes(role)) throw new Error('--role must be owner or admin');\n        if (role === 'owner' && groupId) throw new Error('owner role is always global (do not pass --group)');\n        await getDb().run(\n          `INSERT INTO user_roles (user_id, role, agent_group_id, granted_by, granted_at)\n             VALUES (?, ?, ?, ?, ?)\n             ON CONFLICT DO NOTHING`,\n          userId,\n          role,\n          groupId,\n          grantedBy,\n          new Date().toISOString(),\n        );\n        return { user_id: userId, role, agent_group_id: groupId };\n      },\n    },\n    revoke: {\n      access: 'approval',\n      description: 'Revoke a role. Use --user, --role, and --group if scoped.',","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/nanocoai/nanoclaw/blob/294ef2aee85218b23ad30eda9dfe10e590b54a8c/src/cli/resources/roles.ts#L20-L56","documentation":"Thrown by `ncl roles grant` when the --user flag is missing. Granting a role inserts into user_roles keyed by user_id, which must be the channel-qualified identity (<channel>:<handle>).","triggerScenarios":"Running `ncl roles grant --role owner` without --user; empty user variable in a bootstrap script.","commonSituations":"First-install owner bootstrap where the operator forgets to resolve their channel identity first (see /init-first-agent); using a bare handle instead of the qualified id elsewhere in the same command.","solutions":["Pass --user with the qualified id: `ncl roles grant --user telegram:alice --role owner`","Resolve your identity first via `ncl users list` or the /init-first-agent flow"],"exampleFix":"// before\nncl roles grant --role owner\n// after\nncl roles grant --user telegram:alice --role owner","handlingStrategy":"validation","validationCode":"if (!userId?.includes(':')) throw new Error('--user must be <channel>:<handle>');","typeGuard":"const isQualifiedUserId = (s) => /^[a-z0-9-]+:[A-Za-z0-9_.-]+$/.test(s);","tryCatchPattern":null,"preventionTips":["Resolve your channel identity before granting roles","Prefer /init-first-agent for owner bootstrap"],"tags":["cli","missing-argument","roles","permissions"],"backgroundTag":"missing-cli-flag","analyzedSha":"294ef2aee85218b23ad30eda9dfe10e590b54a8c","analyzedAt":"2026-08-28T13:59:10.357Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}