{"record":{"id":"73029eb5c633e628","repo":"koala73/worldmonitor","slug":"customer-reference-conflict","errorCode":null,"errorMessage":"CUSTOMER_REFERENCE_CONFLICT","messagePattern":"CUSTOMER_REFERENCE_CONFLICT","errorType":"exception","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/companyMonitoring/companies.ts","lineNumber":231,"sourceCode":"\n    const hasName = Object.prototype.hasOwnProperty.call(patch, \"name\");\n    const hasDomicile = Object.prototype.hasOwnProperty.call(patch, \"domicileCountry\");\n    const hasCustomerReference = Object.prototype.hasOwnProperty.call(patch, \"customerReference\");\n    const normalizedFields = normalizeMonitoredCompanyInput({\n      name: hasName ? patch.name! : company.name,\n      domicileCountry: hasDomicile ? patch.domicileCountry! : company.domicileCountry,\n      customerReference: hasCustomerReference\n        ? patch.customerReference\n        : company.customerReference,\n    });\n    if (hasCustomerReference && normalizedFields.customerReference) {\n      const conflict = await findNoopByCustomerReference(\n        ctx,\n        account.logicalAccountId,\n        normalizedFields.customerReference,\n      );\n      if (conflict && conflict.companyId !== company.companyId) {\n        throw new ConvexError(\"CUSTOMER_REFERENCE_CONFLICT\");\n      }\n    }\n\n    const currentClaims = await ctx.db\n      .query(\"companyMonitoringClaims\")\n      .withIndex(\"by_account_company\", (q) =>\n        q.eq(\"ownerAccountId\", account.logicalAccountId).eq(\"companyId\", company.companyId),\n      )\n      .collect();\n    const currentById = new Map(currentClaims.map((claim) => [claim.claimId, claim]));\n    const removeIds = new Set(\n      removeClaimInputs.map((claimId) => assertLogicalId(\"claim\", String(claimId))),\n    );\n    for (const claimId of removeIds) {\n      if (!currentById.has(claimId)) throw new ConvexError(\"CLAIM_NOT_FOUND\");\n    }\n    if (hasCustomerReference && normalizedFields.customerReference !== company.customerReference) {\n      for (const claim of currentClaims) {","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/convex/companyMonitoring/companies.ts#L213-L249","documentation":"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.","triggerScenarios":"Patching a company's customerReference to a value already assigned to a sibling company (active or paused) under the same owner account.","commonSituations":"Two companies sharing an internal billing/ERP reference; reassigning a reference without first clearing it from the previous company; importing companies with colliding references.","solutions":["Before patching, clear or change the customerReference on the company that currently owns it.","Generate a unique customerReference per company rather than reusing one.","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."],"exampleFix":"// before\nawait update({ companyId: A, patch: { customerReference: \"REF-1\" } }); // REF-1 already on company B\n\n// after\nawait setCompanyStateForOwner({ ownerUserId, companyId: B, state: \"removed\" });\n// or pick a fresh reference\nawait update({ companyId: A, patch: { customerReference: \"REF-2\" } });","handlingStrategy":"validation","validationCode":"// ensure the target customerReference is not used by another active/paused company\nconst owner = await listCompaniesForOwner(ownerUserId);\nconst taken = owner.some((c) => c.companyId !== companyId && c.customerReference === newRef);\nif (taken) throw new Error(\"customerReference already in use\");","typeGuard":"function isCustomerReferenceFree(rows: { companyId: string; customerReference?: string }[], companyId: string, ref: string): boolean {\n  return !rows.some((r) => r.companyId !== companyId && r.customerReference === ref);\n}","tryCatchPattern":"try { await updateCompanyForOwner(...); }\ncatch (err) {\n  if (err instanceof ConvexError && err.message === \"CUSTOMER_REFERENCE_CONFLICT\") {\n    // prompt user for a different reference\n  } else throw err;\n}","preventionTips":["Generate customer references from a unique sequence per account.","Clear a reference on the old owner before reassigning it."],"tags":["convex","uniqueness","conflict","company-monitoring"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}