{"record":{"id":"e3d83411e6fd7f4b","repo":"twentyhq/twenty","slug":"createcompany-did-not-return-an-id","errorCode":null,"errorMessage":"createCompany did not return an id","messagePattern":"createCompany 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/import-opportunity-from-tft.service.ts","lineNumber":41,"sourceCode":"  company: ImportOpportunityFromTftInput['company'],\n): Promise<string | undefined> {\n  const name = isNonEmptyString(company?.name) ? company.name.trim() : undefined;\n  const domain = isNonEmptyString(company?.domain) ? company.domain.trim() : undefined;\n  if (name === undefined && domain === undefined) return undefined;\n\n  if (name !== undefined) {\n    const existing = await findCompanyIdByExactName(client, name);\n    if (existing !== undefined) return existing;\n  }\n\n  const companyData: CoreSchema.CompanyCreateInput = { name: name ?? domain! };\n  if (domain !== undefined) companyData.domainName = { primaryLinkUrl: domain };\n\n  const result = await client.mutation({\n    createCompany: { __args: { data: companyData }, id: true },\n  });\n  const id = result.createCompany?.id;\n  if (id === undefined) throw new Error('createCompany did not return an id');\n  return id;\n}\n\n// Find by primary email, else create — name-only contacts can't be deduped.\nasync function findOrCreatePersonId(\n  client: CoreApiClient,\n  pointOfContact: ImportOpportunityFromTftInput['pointOfContact'],\n  companyId: string | undefined,\n): Promise<string | undefined> {\n  const email = isNonEmptyString(pointOfContact?.email)\n    ? pointOfContact.email.trim()\n    : undefined;\n  const firstName = isNonEmptyString(pointOfContact?.firstName)\n    ? pointOfContact.firstName.trim()\n    : '';\n  const lastName = isNonEmptyString(pointOfContact?.lastName)\n    ? pointOfContact.lastName.trim()\n    : '';","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-apps/internal/twenty-partners/src/modules/opportunity/intake/services/import-opportunity-from-tft.service.ts#L23-L59","documentation":"findOrCreateCompanyId in import-opportunity-from-tft calls client.mutation selecting createCompany.id, then asserts the id is present. The error fires when the mutation resolved at the transport level but the returned createCompany payload is null/missing its id — i.e. the server accepted the request shape but produced no usable record. This is the app's defensive guard against a silent server-side rejection that did not surface as a thrown GraphQL error.","triggerScenarios":"createCompany mutation returns { createCompany: null } or { createCompany: { id: null } }. Causes: RLS/permission denial that nulls the mutation root field; a server-side validation or create-trigger that aborted the insert without raising; the workspace metadata for Company is out of sync so the create no-ops; a partial GraphQL response where errors were present but the SDK returned data anyway.","commonSituations":"Running the TFT import as a user/role lacking Company create permission; workspace metadata drifted after a manifest change; app-server version mismatch where createCompany's return contract changed; concurrent import racing on the same domain dedup key.","solutions":["Inspect the raw mutation response for a top-level errors array or a createCompany: null root — log result before the guard.","Verify the caller's workspace role has createCompany permission and that the Company object is installed/synced in metadata.","Confirm the CoreApiClient is authenticated (valid token) and pointed at the correct workspace.","Re-sync the app metadata and retry; if it persists, reproduce the mutation in the GraphQL playground to see the server-side message."],"exampleFix":"// before\nconst result = await client.mutation({\n  createCompany: { __args: { data: companyData }, id: true },\n});\nconst id = result.createCompany?.id;\nif (id === undefined) throw new Error('createCompany did not return an id');\n\n// after — surface the server's actual reason\nconst result = await client.mutation({\n  createCompany: { __args: { data: companyData }, id: true },\n});\nconst id = result.createCompany?.id;\nif (id === undefined) {\n  throw new Error(\n    `createCompany did not return an id (result=${JSON.stringify(result)})`,\n  );\n}","handlingStrategy":"type-guard","validationCode":"import { isNonEmptyString } from 'twenty-shared';\n\nfunction assertCreateInput(data: { name?: unknown }) {\n  if (!isNonEmptyString(data.name)) {\n    throw new Error('createCompany requires a non-empty name');\n  }\n}\n\nassertCreateInput(companyData); // before client.mutation","typeGuard":"const hasCreatedId = (r: unknown): r is { createCompany: { id: string } } =>\n  typeof r === 'object' && r !== null &&\n  typeof (r as any).createCompany?.id === 'string';\n\nif (!hasCreatedId(result)) {\n  throw new Error(`createCompany returned no id: ${JSON.stringify(result)}`);\n}","tryCatchPattern":"try {\n  const id = await findOrCreateCompanyId(client, name, domain);\n  // use id\n} catch (err) {\n  // The service's outer try/catch already converts this to { ok: false, reason }.\n  // Log the raw result for diagnosis, then surface reason to the caller.\n  return { ok: false, reason: err instanceof Error ? err.message : String(err) };\n}","preventionTips":["Always select id: true on create mutations and assert the id before using it.","Validate required input fields (non-empty name) before the mutation.","Log the raw mutation response when the id is missing — the cause is usually a null payload or a swallowed GraphQL error.","Confirm the caller role has create permission on the target object."],"tags":["graphql","sdk","mutation","partners"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}