nanocoai/nanoclaw · error · Error

--role must be owner or admin

Error message

--role must be owner or admin

What it means

Thrown by `ncl roles grant` when --role is missing or is not one of the two supported values ('owner' or 'admin'). The user_roles table only models these two roles, so anything else is rejected before insert.

Source

Thrown at src/cli/resources/roles.ts:39

      type: 'string',
      description:
        'Null = global (all groups). A specific ID limits the role to that group. Owner must always be null.',
    },
    { name: 'granted_by', type: 'string', description: 'Who granted this role. Informational.' },
    { name: 'granted_at', type: 'string', description: 'Auto-set.' },
  ],
  operations: { list: 'open' },
  customOperations: {
    grant: {
      access: 'approval',
      description: 'Grant a role. Use --user, --role, and optionally --group for scoped admin.',
      handler: async (args) => {
        const userId = args.user as string;
        const role = args.role as string;
        const groupId = (args.group as string) ?? null;
        const grantedBy = (args.granted_by as string) ?? null;
        if (!userId) throw new Error('--user is required');
        if (!role || !['owner', 'admin'].includes(role)) throw new Error('--role must be owner or admin');
        if (role === 'owner' && groupId) throw new Error('owner role is always global (do not pass --group)');
        await getDb().run(
          `INSERT INTO user_roles (user_id, role, agent_group_id, granted_by, granted_at)
             VALUES (?, ?, ?, ?, ?)
             ON CONFLICT DO NOTHING`,
          userId,
          role,
          groupId,
          grantedBy,
          new Date().toISOString(),
        );
        return { user_id: userId, role, agent_group_id: groupId };
      },
    },
    revoke: {
      access: 'approval',
      description: 'Revoke a role. Use --user, --role, and --group if scoped.',
      handler: async (args) => {

View on GitHub (pinned to 294ef2aee8)

Solutions

  1. Use exactly `--role owner` or `--role admin` (lowercase)
  2. For unprivileged access, use `ncl members add` instead of a role
  3. For scoped admin, add --group <gid>; for owner, omit --group

Example fix

// before
ncl roles grant --user telegram:alice --role member
// after
ncl roles grant --user telegram:alice --role admin --group grp1
Defensive patterns

Strategy: type-guard

Validate before calling

const ROLES = ['owner', 'admin'] as const; if (!ROLES.includes(role)) throw new Error(`--role must be one of ${ROLES.join('|')}`);

Type guard

const isValidRole = (r: unknown): r is 'owner' | 'admin' => r === 'owner' || r === 'admin';

Prevention

When it happens

Trigger: Omitting --role entirely; passing 'member', 'moderator', ' Approver', or another string the whitelist does not include.

Common situations: Operators coming from systems with richer role models try to invent roles; quoting/case mistakes in scripts produce values like 'Admin' that fail the exact-match check.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28). Data as JSON: /api/errors/82eebd7eed280d4e. Report an issue: GitHub.