twentyhq/twenty · error · Error
on-opportunity-partner-assigned: ${failed} person stamp(s) f
Error message
on-opportunity-partner-assigned: ${failed} person stamp(s) failed — retrying What it means
propagatePartnerUser's assign/reassign path runs all Person partnerUser-stamps via Promise.allSettled and throws if any rejected. Same idempotent-retry design as the clear path: re-running re-collects people for the company and re-stamps only those still missing the partnerUser, so a thrown error safely drives a retry rather than leaving a partial stamp.
Source
Thrown at packages/twenty-apps/internal/twenty-partners/src/modules/opportunity/matching/services/propagate-partner-user.service.ts:141
const companyId = after?.companyId;
if (!companyId) return { cascaded: true, partnerUserId };
// Don't clobber a company already owned by a DIFFERENT partner member. The single
// partnerUser column on Company/Person models one owner per company, so reassigning it
// here would steal the company (and its contacts) from the other partner and expose their
// data. Stamp only the opportunity in that case and leave the company/people alone.
const companyResult = await getCompanyPartnerUser(client, companyId);
const companyOwner = companyResult.company?.partnerUserId;
if (companyOwner && companyOwner !== partnerUserId) {
return { cascaded: true, partnerUserId, companyShared: true };
}
await updateCompanyPartnerUser(client, companyId, partnerUserId);
const peopleIds = await collectPeopleIds(client, { companyId: { eq: companyId } });
const failed = await setPeoplePartnerUser(client, peopleIds, partnerUserId);
if (failed > 0) {
throw new Error(
`on-opportunity-partner-assigned: ${failed} person stamp(s) failed — retrying`,
);
}
return { cascaded: true, partnerUserId };
}
View on GitHub (pinned to 1f5dd2bbd2)
Solutions
- Retry — the function is idempotent (assign path re-stamps all people for the company; already-stamped rows are a no-op).
- If persistent, log which personIds rejected to isolate the record.
- Check app-server logs for 5xx or RLS denials on updatePerson.
- Verify the partnerUser field on Person is not field-locked for the app identity running the cascade.
Example fix
// before
const failed = await setPeoplePartnerUser(client, peopleIds, partnerUserId);
if (failed > 0) {
throw new Error(`on-opportunity-partner-assigned: ${failed} person stamp(s) failed — retrying`);
}
// after — keep the throw for retry; log failures for diagnosis
const failedIds = await setPeoplePartnerUserDetailed(client, peopleIds, partnerUserId);
if (failedIds.length > 0) {
console.error(`person stamp failures for ${opportunityId}/${partnerId}:`, failedIds);
throw new Error(`on-opportunity-partner-assigned: ${failedIds.length} person stamp(s) failed — retrying`);
} Defensive patterns
Strategy: retry
Validate before calling
// No client-side pre-validation prevents transient mid-cascade rejections. // Ensure the workflow engine retries thrown logic-function errors.
Try / catch
// propagatePartnerUser is idempotent on the assign path too (re-stamps all
// people for the company; already-stamped rows are a no-op). Retry on throw:
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await propagatePartnerUser(client, params);
} catch (err) {
if (attempt === 2) throw err;
await new Promise((r) => setTimeout(r, 2 ** attempt * 500));
}
} Prevention
- Rely on idempotent re-runs — already-stamped people are not duplicated.
- Ensure the workflow engine retries thrown errors with backoff.
- Confirm the partnerUser field on Person is not field-locked for the app identity.
- Monitor app-server logs for RLS denials/5xx on updatePerson during cascades.
When it happens
Trigger: One or more updatePersonPartnerUser(client, id, partnerUserId) promises reject during the assign cascade. Causes: transient network/5xx; Person deleted mid-cascade; RLS field-lock; concurrent reassignment racing on the same company's people.
Common situations: Assigning a partner to an opportunity on a company with many contacts; a contact concurrently edited/deleted; app-server restart mid-batch; field-lock misconfiguration rejecting the stamp.
Related errors
- on-opportunity-partner-assigned: ${failed} person clear(s) f
- link-partner-user: ${failed} cascade write(s) failed for ${p
- 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/83d02051f53ca4a4.
Report an issue: GitHub.