{"record":{"id":"aeeecf22c37949b9","repo":"twentyhq/twenty","slug":"createopportunity-did-not-return-an-id-aeeecf","errorCode":null,"errorMessage":"createOpportunity did not return an id","messagePattern":"createOpportunity did not return an id","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/twenty-apps/internal/twenty-partners/src/modules/opportunity/intake/services/submit-client-brief.service.ts","lineNumber":70,"sourceCode":"    const opportunityData: CoreSchema.OpportunityCreateInput = {\n      name,\n      need: input.need,\n      isListed: false,\n      stage: 'NEW',\n      companyId,\n      pointOfContactId,\n    };\n    if (requirements !== null) {\n      opportunityData.requirements = requirements;\n    }\n    if (referringPartner !== null) {\n      opportunityData.referredByPartnerId = referringPartner.id;\n    }\n\n    const result = await createOpportunity(client, opportunityData);\n    const opportunityId = result.createOpportunity?.id;\n    if (opportunityId === undefined) {\n      throw new Error('createOpportunity did not return an id');\n    }\n\n    await notifyClientBrief({ opportunityId, input, referringPartner });\n\n    return { ok: true, opportunityId };\n  } catch (err) {\n    return { ok: false, reason: err instanceof Error ? err.message : String(err) };\n  }\n}\n","sourceCodeStart":52,"sourceCodeEnd":80,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-apps/internal/twenty-partners/src/modules/opportunity/intake/services/submit-client-brief.service.ts#L52-L80","documentation":"submitClientBrief calls createOpportunity and asserts createOpportunity.id before notifying on the new opportunity. Same guard shape as the TFT importer: the server returned a null/empty createOpportunity instead of throwing. The service's outer try/catch converts it to { ok: false, reason }.","triggerScenarios":"createOpportunity returns null or an id-less payload. Triggered by missing required fields in opportunityData, an invalid referringPartnerId/referredByPartnerId relation, server-side validation failure, or insufficient Opportunity create permission.","commonSituations":"Submitting a client brief with incomplete requirements or an unknown referring partner; role lacks Opportunity create; metadata drift; amount/stage outside allowed value list.","solutions":["Check the returned SubmitClientBriefResult.reason — it echoes this message.","Inspect the raw createOpportunity response (null payload vs. errors array).","Validate opportunityData required fields and relation ids (companyId, referredByPartnerId) before the call.","Confirm the caller's role has Opportunity create permission and the object is synced."],"exampleFix":"// before\nconst result = await createOpportunity(client, opportunityData);\nconst opportunityId = result.createOpportunity?.id;\nif (opportunityId === undefined) {\n  throw new Error('createOpportunity did not return an id');\n}\n\n// after — richer reason for the caller\nconst result = await createOpportunity(client, opportunityData);\nconst opportunityId = result.createOpportunity?.id;\nif (opportunityId === undefined) {\n  throw new Error(\n    `createOpportunity did not return an id (referringPartner=${referringPartner?.id ?? 'none'}, result=${JSON.stringify(result)})`,\n  );\n}","handlingStrategy":"try-catch","validationCode":"function assertClientBriefOpportunityData(data: Record<string, unknown>) {\n  if (!data.stage) throw new Error('opportunityData.stage is required');\n  if (requirements !== null) data.requirements = requirements; // ensure non-null\n}\nassertClientBriefOpportunityData(opportunityData);","typeGuard":"const hasCreatedOpportunityId = (r: unknown): r is { createOpportunity: { id: string } } =>\n  typeof r === 'object' && r !== null &&\n  typeof (r as any).createOpportunity?.id === 'string';\n\nif (!hasCreatedOpportunityId(result)) {\n  throw new Error(`createOpportunity returned no id: ${JSON.stringify(result)}`);\n}","tryCatchPattern":"// submitClientBrief already wraps this in try/catch returning { ok, reason }.\n// Callers branch on result.ok:\nconst res = await submitClientBrief(input);\nif (!res.ok) {\n  logger.warn('client brief submit failed', { reason: res.reason });\n  return;\n}","preventionTips":["Check SubmitClientBriefResult.ok before using opportunityId.","Validate opportunity required fields and relation ids (companyId, referredByPartnerId) before the create.","Log the raw createOpportunity response when the id is missing.","Confirm Opportunity create permission."],"tags":["graphql","sdk","mutation","partners","opportunity"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}