twentyhq/twenty · error · Error

createCompany did not return an id

Error message

createCompany did not return an id

What it means

findOrCreateCompanyByName in the shared service guards createCompany(client, name) result. After a name-dedup lookup, it creates and asserts createCompany.id. An undefined id means the server returned a null/id-less company payload instead of throwing — the shared variant of the company guard used across modules.

Source

Thrown at packages/twenty-apps/internal/twenty-partners/src/modules/shared/services/find-or-create-company-and-person.service.ts:29

): Promise<string | undefined> {
  const name = companyName.trim();

  const lookup = await findCompanyByName(client, name);

  return lookup.companies?.edges?.[0]?.node?.id;
}

export async function findOrCreateCompanyByName(
  client: CoreApiClient,
  companyName: string,
): Promise<string> {
  const existing = await findCompanyIdByExactName(client, companyName);
  if (existing !== undefined) return existing;

  const name = companyName.trim();
  const result = await createCompany(client, name);
  const id = result.createCompany?.id;
  if (id === undefined) throw new Error('createCompany did not return an id');
  return id;
}

export async function findPersonIdByPrimaryEmail(
  client: CoreApiClient,
  email: string,
): Promise<string | undefined> {
  const primaryEmail = email.trim();

  const lookup = await findPersonByEmail(client, primaryEmail);

  return lookup.people?.edges?.[0]?.node?.id;
}

export type FindOrCreatePersonByEmailInput = {
  email: string;
  firstName: string;
  lastName: string;

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Inspect the raw createCompany response for null payload or errors array.
  2. Validate companyName.trim() is non-empty at the call site before invoking findOrCreateCompanyByName.
  3. Confirm the caller's role has Company create permission and Company is synced.
  4. Reproduce createCompany in the GraphQL playground to read the server rejection.

Example fix

// before
const result = await createCompany(client, name);
const id = result.createCompany?.id;
if (id === undefined) throw new Error('createCompany did not return an id');

// after — guard input and surface result
if (!companyName.trim()) throw new Error('companyName must be non-empty');
const result = await createCompany(client, companyName.trim());
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';

export async function findOrCreateCompanyByNameSafe(client: CoreApiClient, companyName: string) {
  if (!isNonEmptyString(companyName)) throw new Error('companyName must be non-empty');
  return findOrCreateCompanyByName(client, companyName);
}

Type guard

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

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

Prevention

When it happens

Trigger: createCompany returns { createCompany: null } or no id. Causes: caller lacks Company create permission; name empty after trim; Company metadata out of sync; server-side validation; concurrent create beating the dedup lookup.

Common situations: Call site passing a whitespace-only companyName; role lacks Company create; manifest changed Company and metadata not re-synced; race creating the same company twice.

Related errors


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