koala73/worldmonitor · error · ConvexError
CLAIM_NOT_FOUND
Error message
CLAIM_NOT_FOUND
What it means
Thrown by updateCompanyForOwner when a claim id listed in removeClaimIds does not correspond to any existing claim on the target company. Each removal id is validated against the by_account_company claims map (currentById) built from the live claim rows, so the id must be a real claimId owned by that company.
Source
Thrown at convex/companyMonitoring/companies.ts:246
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) {
if (claim.type === "customer_reference") removeIds.add(claim.claimId);
}
}
const remainingKeys = new Set(
currentClaims
.filter((claim) => !removeIds.has(claim.claimId))
.map((claim) => `${claim.type}\u0000${claim.value}`),
);
const normalizedAdditions = addClaimInputs.map(normalizeCompanyClaimInput);
if (normalizedFields.name !== company.name) {
normalizedAdditions.unshift({ type: "alias", value: normalizedFields.name });
}
if (hasCustomerReference && normalizedFields.customerReference) {
normalizedAdditions.push({View on GitHub (pinned to ffec79ac33)
Solutions
- Refresh the company's current claims before issuing the removal patch.
- Tolerate already-removed claims client-side by filtering the removeClaimIds to only those still present.
- If the removal is best-effort, split removals so a missing one does not abort the whole patch.
Example fix
// before
await update({ companyId, patch: { removeClaimIds: cachedIds } });
// after
const claims = await listClaims(companyId);
const stillPresent = cachedIds.filter((id) => claims.some((c) => c.claimId === id));
await update({ companyId, patch: { removeClaimIds: stillPresent } }); Defensive patterns
Strategy: validation
Validate before calling
const current = await listClaimsForCompany(ownerUserId, companyId);
const knownIds = new Set(current.map((c) => c.claimId));
const validRemovals = removeClaimIds.filter((id) => knownIds.has(id));
await updateCompanyForOwner({ ownerUserId, companyId, patch: { removeClaimIds: validRemovals } }); Type guard
function everyRemovalExists(current: { claimId: string }[], removals: string[]): boolean {
const known = new Set(current.map((c) => c.claimId));
return removals.every((id) => known.has(id));
} Prevention
- Refresh the claim list before issuing removals.
- Filter client-side to ids still present to tolerate concurrent edits.
When it happens
Trigger: Passing a removeClaimIds entry that is not a current claimId of the patched company: a stale id, an id from a different company, an id already removed in a concurrent update, or a malformed logical id.
Common situations: UI caching a claim list that has since changed; concurrent edits where one removes a claim another is also removing; copy/paste of a claim id across companies.
Related errors
AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12).
Data as JSON: /api/errors/5654f7939986bb82.
Report an issue: GitHub.