stablyai/orca · error · Error

invalid_orca_org_role

Error message

invalid_orca_org_role

What it means

Thrown by orgRoleFromUnknown() when the role value on an invite or role-change payload is not one of the literal string values 'owner', 'admin', or 'member'. The function narrows an untrusted renderer input to the OrcaOrgRole union; any other value (including typos like 'Owner' with capital, 'guest', numbers, or undefined) is rejected before reaching the cloud org-members service.

Source

Thrown at src/main/ipc/orca-profile-org-members-handlers.ts:37

} from '../orca-profiles/profile-cloud-org-members-service'

function orgMembersScopedArgs(args: unknown): { orgId: string; record: Record<string, unknown> } {
  if (!args || typeof args !== 'object') {
    throw new Error('invalid_orca_profile_org_selection')
  }
  const record = args as Record<string, unknown>
  const orgId = typeof record.orgId === 'string' ? record.orgId.trim() : ''
  if (!orgId) {
    throw new Error('invalid_orca_profile_org_selection')
  }
  return { orgId, record }
}

function orgRoleFromUnknown(value: unknown): OrcaOrgRole {
  if (value === 'owner' || value === 'admin' || value === 'member') {
    return value
  }
  throw new Error('invalid_orca_org_role')
}

function orgEmailFromUnknown(value: unknown): string {
  const email = typeof value === 'string' ? value.trim() : ''
  if (!email) {
    throw new Error('invalid_orca_org_member_email')
  }
  return email
}

function orgUserIdFromUnknown(value: unknown): string {
  const userId = typeof value === 'string' ? value.trim() : ''
  if (!userId) {
    throw new Error('invalid_orca_org_member_user')
  }
  return userId
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Send the lowercase canonical role string exactly: 'owner' | 'admin' | 'member' — no capitalization, no synonyms.
  2. Map UI role labels to canonical ids at the dropdown boundary and pass the id, not the label.
  3. If a new role is genuinely supported upstream, add it to the literal union in shared/orca-profiles (OrcaOrgRole) and to this guard in lockstep.
  4. Default the invite role to 'member' when the picker has no explicit selection, rather than sending undefined.

Example fix

// before
await ipcRenderer.invoke('orcaProfiles:orgMemberInvite', { orgId, email, role: roleSelect.value })

// after
const ROLES = ['owner', 'admin', 'member'] as const
const role = ROLES.includes(roleSelect.value) ? roleSelect.value : 'member'
await ipcRenderer.invoke('orcaProfiles:orgMemberInvite', { orgId, email, role })
Defensive patterns

Strategy: type-guard

Validate before calling

const ORG_ROLES = ['owner', 'admin', 'member'] as const
type OrgRole = typeof ORG_ROLES[number]

function toOrgRole(v: unknown): OrgRole {
  return ORG_ROLES.includes(v as OrgRole) ? (v as OrgRole) : 'member'
}

Type guard

function isOrgRole(v: unknown): v is 'owner' | 'admin' | 'member' {
  return v === 'owner' || v === 'admin' || v === 'member'
}

Try / catch

try {
  await ipcRenderer.invoke('orcaProfiles:orgMemberInvite', { orgId, email, role })
} catch (e) {
  if (e instanceof Error && e.message === 'invalid_orca_org_role') {
    setRoleError('Role must be owner, admin, or member.')
  } else throw e
}

Prevention

When it happens

Trigger: Calling orcaProfiles:orgMemberInvite or orcaProfiles:orgMemberChangeRole with a role field that is missing, undefined, or any string other than exactly 'owner', 'admin', or 'member'. Capitalized variants and synonyms are rejected.

Common situations: Role dropdown emits a display label ('Administrator') instead of the canonical value, a serialized enum from a different schema leaks in, or a new role was added to the cloud API but the main-process allowlist has not been updated.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/fbc09b7a16bb5824. Report an issue: GitHub.