koala73/worldmonitor · error · ConvexError

CUSTOMER_REFERENCE_CONFLICT

Error message

CUSTOMER_REFERENCE_CONFLICT

What it means

Thrown by updateCompanyForOwner when setting a customerReference that another active or paused company in the same account already uses. The duplicate check (findNoopByCustomerReference) queries the by_account_customerReference_lifecycle index for active then paused rows, and the conflict is reported only when the matched row is a different companyId than the one being patched.

Source

Thrown at convex/companyMonitoring/companies.ts:231

    const hasName = Object.prototype.hasOwnProperty.call(patch, "name");
    const hasDomicile = Object.prototype.hasOwnProperty.call(patch, "domicileCountry");
    const hasCustomerReference = Object.prototype.hasOwnProperty.call(patch, "customerReference");
    const normalizedFields = normalizeMonitoredCompanyInput({
      name: hasName ? patch.name! : company.name,
      domicileCountry: hasDomicile ? patch.domicileCountry! : company.domicileCountry,
      customerReference: hasCustomerReference
        ? patch.customerReference
        : company.customerReference,
    });
    if (hasCustomerReference && normalizedFields.customerReference) {
      const conflict = await findNoopByCustomerReference(
        ctx,
        account.logicalAccountId,
        normalizedFields.customerReference,
      );
      if (conflict && conflict.companyId !== company.companyId) {
        throw new ConvexError("CUSTOMER_REFERENCE_CONFLICT");
      }
    }

    const currentClaims = await ctx.db
      .query("companyMonitoringClaims")
      .withIndex("by_account_company", (q) =>
        q.eq("ownerAccountId", account.logicalAccountId).eq("companyId", company.companyId),
      )
      .collect();
    const currentById = new Map(currentClaims.map((claim) => [claim.claimId, claim]));
    const removeIds = new Set(
      removeClaimInputs.map((claimId) => assertLogicalId("claim", String(claimId))),
    );
    for (const claimId of removeIds) {
      if (!currentById.has(claimId)) throw new ConvexError("CLAIM_NOT_FOUND");
    }
    if (hasCustomerReference && normalizedFields.customerReference !== company.customerReference) {
      for (const claim of currentClaims) {

View on GitHub (pinned to ffec79ac33)

Solutions

  1. Before patching, clear or change the customerReference on the company that currently owns it.
  2. Generate a unique customerReference per company rather than reusing one.
  3. If the reuse is intentional (the other company is no longer needed), set the other company to 'removed' first so findNoopByCustomerReference no longer matches it.

Example fix

// before
await update({ companyId: A, patch: { customerReference: "REF-1" } }); // REF-1 already on company B

// after
await setCompanyStateForOwner({ ownerUserId, companyId: B, state: "removed" });
// or pick a fresh reference
await update({ companyId: A, patch: { customerReference: "REF-2" } });
Defensive patterns

Strategy: validation

Validate before calling

// ensure the target customerReference is not used by another active/paused company
const owner = await listCompaniesForOwner(ownerUserId);
const taken = owner.some((c) => c.companyId !== companyId && c.customerReference === newRef);
if (taken) throw new Error("customerReference already in use");

Type guard

function isCustomerReferenceFree(rows: { companyId: string; customerReference?: string }[], companyId: string, ref: string): boolean {
  return !rows.some((r) => r.companyId !== companyId && r.customerReference === ref);
}

Try / catch

try { await updateCompanyForOwner(...); }
catch (err) {
  if (err instanceof ConvexError && err.message === "CUSTOMER_REFERENCE_CONFLICT") {
    // prompt user for a different reference
  } else throw err;
}

Prevention

When it happens

Trigger: Patching a company's customerReference to a value already assigned to a sibling company (active or paused) under the same owner account.

Common situations: Two companies sharing an internal billing/ERP reference; reassigning a reference without first clearing it from the previous company; importing companies with colliding references.

Related errors


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/73029eb5c633e628. Report an issue: GitHub.