twentyhq/twenty · error · Error

Could not find an available callRecordingOld name after ${MA

Error message

Could not find an available callRecordingOld name after ${MAX_OLD_NAME_ATTEMPTS} attempts

What it means

Thrown by resolveAvailableOldCallRecordingObjectNames after trying 100 candidate names (callRecordingOld, callRecordingOld2 ... callRecordingOld101) and finding every singular/plural pair already taken in the workspace's object metadata plus the additional reserved set. MAX_OLD_NAME_ATTEMPTS is 100.

Source

Thrown at packages/twenty-server/src/database/commands/upgrade-version-command/2-10/utils/call-recording-name-collision.util.ts:83

  for (let attempt = 0; attempt < MAX_OLD_NAME_ATTEMPTS; attempt++) {
    const discriminator = attempt === 0 ? '' : `${attempt + 1}`;
    const nameSingular = `${CALL_RECORDING_OLD_NAME_SINGULAR}${discriminator}`;
    const namePlural = `${CALL_RECORDING_OLD_NAME_PLURAL}${discriminator}`;

    if (!takenNames.has(nameSingular) && !takenNames.has(namePlural)) {
      const labelSuffix = discriminator === '' ? '' : ` ${discriminator}`;

      return {
        nameSingular,
        namePlural,
        labelSingular: `Call Recording (Old)${labelSuffix}`,
        labelPlural: `Call Recordings (Old)${labelSuffix}`,
      };
    }
  }

  throw new Error(
    `Could not find an available callRecordingOld name after ${MAX_OLD_NAME_ATTEMPTS} attempts`,
  );
};

export const buildCallRecordingObjectRenameUpdates = ({
  flatObjectMetadataMaps,
  now,
}: {
  flatObjectMetadataMaps: FlatEntityMaps<FlatObjectMetadata>;
  now: string;
}): FlatObjectMetadata[] => {
  const reservedOldNames = new Set<string>();

  return findCallRecordingObjectNameCollisions(flatObjectMetadataMaps).map(
    (collidingObjectMetadata) => {
      const { nameSingular, namePlural, labelSingular, labelPlural } =
        resolveAvailableOldCallRecordingObjectNames(
          flatObjectMetadataMaps,

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Query the workspace for objects whose name matches /^callRecording(Old|sOld)\d*$/ and manually rename or delete the surplus.
  2. Re-run the command after freeing names; collision resolution will then find a slot.
  3. If genuinely needed, raise MAX_OLD_NAME_ATTEMPTS in the util and re-run (last resort).

Example fix

// before: relies on 100 attempts silently throwing
const names = resolveAvailableOldCallRecordingObjectNames(maps, reserved);

// after: pre-check capacity and surface a clear signal
const freeCapacity = 100 - countNamesMatching(maps, /^callRecording(Old|sOld)\d*$/);
if (freeCapacity <= 0) {
  throw new Error('No free callRecordingOld slots; clean up duplicate objects first');
}
Defensive patterns

Strategy: validation

Validate before calling

import { resolveAvailableOldCallRecordingObjectNames } from '../utils/call-recording-name-collision.util';

// Before calling, count existing callRecordingOld* names to ensure a slot will be found:
const takenCount = Object.values(flatObjectMetadataMaps.byUniversalIdentifier)
  .filter(isDefined)
  .filter((o) => /^callRecording(Old|sOld)\d*$/.test(o.nameSingular) || /^callRecording(s)?Old\d*$/.test(o.namePlural))
  .length;
if (takenCount >= 100) {
  throw new Error('callRecordingOld namespace exhausted; remove duplicate objects before retrying');
}
const names = resolveAvailableOldCallRecordingObjectNames(flatObjectMetadataMaps, reservedOldNames);

Prevention

When it happens

Trigger: A workspace has 100+ existing objects whose nameSingular/namePlural cover the entire callRecordingOld<number> range, so no free slot remains for renaming a colliding callRecording object.

Common situations: Extremely rare: requires deliberate or pathological creation of 100+ objects with those exact names; re-running the migration many times against an already-collision-heavy workspace; a corrupted workspace with mass duplicate objects.

Related errors


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