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
- 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.
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
- 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.
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
- createPerson did not return an id
- createOpportunity did not return an id
- createOpportunity did not return an id
- createCompany did not return an id
- createPartner did not return an id
AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12).
Data as JSON: /api/errors/e3d83411e6fd7f4b.
Report an issue: GitHub.