twentyhq/twenty · error · Error
Partner role not found. Available roles: ${labels}. Ensure t
Error message
Partner role not found. Available roles: ${labels}. Ensure the app is installed (universalIdentifier=${PARTNER_ROLE_UNIVERSAL_IDENTIFIER}). What it means
configure-partner-rls fetches all roles and finds the one whose label equals PARTNER_ROLE_LABEL (the role shipped by the app manifest). If none matches, it throws listing the available role labels and the app's universalIdentifier. The Partner role must exist before the script can attach RLS predicates to it.
Source
Thrown at packages/twenty-apps/internal/twenty-partners/src/scripts/configure-partner-rls.ts:389
// Fetching fieldPermissions here avoids a second getRoles call later in step 5.
const rolesData = await metadataFetch<{
getRoles: {
id: string;
label: string;
fieldPermissions: FieldPermissionResult[];
}[];
}>(
metadataUrl,
apiKey,
`{ getRoles { id label fieldPermissions { id fieldMetadataId objectMetadataId canUpdateFieldValue canReadFieldValue } } }`,
);
const roles = rolesData.getRoles;
const partnerRole = roles.find((r) => r.label === PARTNER_ROLE_LABEL);
if (!partnerRole) {
const labels = roles.map((r) => r.label).join(', ');
throw new Error(
`Partner role not found. Available roles: ${labels}. ` +
`Ensure the app is installed (universalIdentifier=${PARTNER_ROLE_UNIVERSAL_IDENTIFIER}).`,
);
}
console.log(
`[rls:configure] Partner role id: ${partnerRole.id} ` +
`(universalIdentifier in manifest: ${PARTNER_ROLE_UNIVERSAL_IDENTIFIER})`,
);
// ── 4. Upsert one predicate per object ───────────────────────────────────────
//
// "the record's partnerUser relation IS the current workspace member". Operand must be IS,
// not CONTAINS: the upsert accepts CONTAINS but the RELATION query filter only allows
// IS / IS_NOT and throws "Unknown operand CONTAINS for RELATION filter" at query time.
// value stays null; workspaceMemberFieldMetadataId injects the current member at query time.
const MUTATION = `View on GitHub (pinned to 1f5dd2bbd2)
Solutions
- Install/re-sync the partners app so its PARTNER_ROLE lands in the workspace (the message prints the expected universalIdentifier).
- Confirm TWENTY_PARTNERS_API_URL/TWENTY_PARTNERS_API_KEY point at the correct workspace.
- Compare the printed 'Available roles' against PARTNER_ROLE_LABEL to catch a rename.
- Re-run rls:configure after the role appears (idempotent).
Example fix
// before
if (!partnerRole) {
const labels = roles.map((r) => r.label).join(', ');
throw new Error(`Partner role not found. Available roles: ${labels}. Ensure the app is installed (universalIdentifier=${PARTNER_ROLE_UNIVERSAL_IDENTIFIER}).`);
}
// after — also match by universalIdentifier as a fallback and report both
const partnerRole =
roles.find((r) => r.label === PARTNER_ROLE_LABEL) ??
roles.find((r) => r.universalIdentifier === PARTNER_ROLE_UNIVERSAL_IDENTIFIER);
if (!partnerRole) {
const labels = roles.map((r) => `${r.label}(${r.universalIdentifier ?? 'no-uid'})`).join(', ');
throw new Error(`Partner role not found (label=${PARTNER_ROLE_LABEL}, uid=${PARTNER_ROLE_UNIVERSAL_IDENTIFIER}). Available roles: ${labels}.`);
} Defensive patterns
Strategy: validation
Validate before calling
const roles = await fetchRoles(metadataUrl, apiKey);
const partnerRole =
roles.find((r) => r.label === PARTNER_ROLE_LABEL) ??
roles.find((r) => r.universalIdentifier === PARTNER_ROLE_UNIVERSAL_IDENTIFIER);
if (!partnerRole) {
throw new Error(`Install/sync the partners app so the Partner role exists (uid=${PARTNER_ROLE_UNIVERSAL_IDENTIFIER})`);
} Prevention
- Install and sync the partners app before rls:configure — the Partner role ships from the manifest.
- Confirm the target workspace matches the app's universalIdentifier (PARTNER_ROLE_UNIVERSAL_IDENTIFIER must stay byte-identical).
- Compare the printed 'Available roles' to PARTNER_ROLE_LABEL to catch renames.
- Re-run rls:configure after the role appears (idempotent).
When it happens
Trigger: getRoles returns a list with no role.label === PARTNER_ROLE_LABEL. Causes: the app isn't installed (the role comes from the manifest); the app is installed but roles haven't synced; the role label was changed; querying the wrong workspace; the manifest's universalIdentifier doesn't match PARTNER_ROLE_UNIVERSAL_IDENTIFIER.
Common situations: Running rls:configure before/without installing the app; partial install where objects synced but roles didn't; wrong workspace; app version that renamed the role; manually deleted the Partner role.
Related errors
- Object "${name}" not found in workspace metadata. Has the ap
- workspaceMember object not found in workspace metadata.
- Missing env var: ${name}
- GraphQL errors: ${json.errors.map((e) => e.message).join(';
- Field "${fieldName}" not found on object "${objectName}" aft
AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12).
Data as JSON: /api/errors/f2dbb2e1052e2cf1.
Report an issue: GitHub.