twentyhq/twenty · error · Error

createCompany did not return an id

Error message

createCompany did not return an id

What it means

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.

Source

Thrown at packages/twenty-apps/internal/twenty-partners/src/modules/opportunity/intake/services/import-opportunity-from-tft.service.ts:41

  company: ImportOpportunityFromTftInput['company'],
): Promise<string | undefined> {
  const name = isNonEmptyString(company?.name) ? company.name.trim() : undefined;
  const domain = isNonEmptyString(company?.domain) ? company.domain.trim() : undefined;
  if (name === undefined && domain === undefined) return undefined;

  if (name !== undefined) {
    const existing = await findCompanyIdByExactName(client, name);
    if (existing !== undefined) return existing;
  }

  const companyData: CoreSchema.CompanyCreateInput = { name: name ?? domain! };
  if (domain !== undefined) companyData.domainName = { primaryLinkUrl: domain };

  const result = await client.mutation({
    createCompany: { __args: { data: companyData }, id: true },
  });
  const id = result.createCompany?.id;
  if (id === undefined) throw new Error('createCompany did not return an id');
  return id;
}

// Find by primary email, else create — name-only contacts can't be deduped.
async function findOrCreatePersonId(
  client: CoreApiClient,
  pointOfContact: ImportOpportunityFromTftInput['pointOfContact'],
  companyId: string | undefined,
): Promise<string | undefined> {
  const email = isNonEmptyString(pointOfContact?.email)
    ? pointOfContact.email.trim()
    : undefined;
  const firstName = isNonEmptyString(pointOfContact?.firstName)
    ? pointOfContact.firstName.trim()
    : '';
  const lastName = isNonEmptyString(pointOfContact?.lastName)
    ? pointOfContact.lastName.trim()
    : '';

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Inspect the raw mutation response for a top-level errors array or a createCompany: null root — log result before the guard.
  2. Verify the caller's workspace role has createCompany permission and that the Company object is installed/synced in metadata.
  3. Confirm the CoreApiClient is authenticated (valid token) and pointed at the correct workspace.
  4. Re-sync the app metadata and retry; if it persists, reproduce the mutation in the GraphQL playground to see the server-side message.

Example fix

// before
const result = await client.mutation({
  createCompany: { __args: { data: companyData }, id: true },
});
const id = result.createCompany?.id;
if (id === undefined) throw new Error('createCompany did not return an id');

// after — surface the server's actual reason
const result = await client.mutation({
  createCompany: { __args: { data: companyData }, id: true },
});
const id = result.createCompany?.id;
if (id === undefined) {
  throw new Error(
    `createCompany did not return an id (result=${JSON.stringify(result)})`,
  );
}
Defensive patterns

Strategy: type-guard

Validate before calling

import { isNonEmptyString } from 'twenty-shared';

function assertCreateInput(data: { name?: unknown }) {
  if (!isNonEmptyString(data.name)) {
    throw new Error('createCompany requires a non-empty name');
  }
}

assertCreateInput(companyData); // before client.mutation

Type guard

const hasCreatedId = (r: unknown): r is { createCompany: { id: string } } =>
  typeof r === 'object' && r !== null &&
  typeof (r as any).createCompany?.id === 'string';

if (!hasCreatedId(result)) {
  throw new Error(`createCompany returned no id: ${JSON.stringify(result)}`);
}

Try / catch

try {
  const id = await findOrCreateCompanyId(client, name, domain);
  // use id
} catch (err) {
  // The service's outer try/catch already converts this to { ok: false, reason }.
  // Log the raw result for diagnosis, then surface reason to the caller.
  return { ok: false, reason: err instanceof Error ? err.message : String(err) };
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12). Data as JSON: /api/errors/e3d83411e6fd7f4b. Report an issue: GitHub.