koala73/worldmonitor · error · ConvexError

CLAIM_NOT_FOUND

CLAIM_NOT_FOUND

Error message

CLAIM_NOT_FOUND

What it means

Thrown by the company update mutation (convex/companyMonitoring/companies.ts:246) when patch.removeClaimIds contains a claimId that does not exist among the company's current claims (queried via by_account_company). Every id in removeClaimIds must resolve to an existing claim, and ids are also run through assertLogicalId first.

Solutions

  1. Re-fetch the company's current claims and filter removeClaimIds to existing ids before submitting
  2. If your UX treats removing-an-already-removed claim as a no-op, compute the intersection and drop missing ids client-side
  3. Never send add and remove for the same claim value in one patch; separate the calls
  4. Reload claim state after any failed patch instead of resubmitting verbatim

Example fix

// before
await api.companyMonitoring.updateCompany({
  ownerUserId, companyId,
  patch: { removeClaimIds: selectedClaimIds }, // may include already-deleted ids
});

// after
const current = await api.companyMonitoring.listClaims({ ownerUserId, companyId });
const currentIds = new Set(current.map((c) => c.claimId));
const removeClaimIds = selectedClaimIds.filter((id) => currentIds.has(id)); // missing ids become no-ops
if (removeClaimIds.length > 0) {
  await api.companyMonitoring.updateCompany({ ownerUserId, companyId, patch: { removeClaimIds } });
}
Defensive patterns

Strategy: validation

Validate before calling

const current = await api.companyMonitoring.listClaims({ ownerUserId, companyId });
const currentIds = new Set(current.map((c) => c.claimId));
const removeClaimIds = requestedRemoveIds.filter((id) => currentIds.has(id)); // missing ids become no-ops
if (removeClaimIds.length > 0) {
  await api.companyMonitoring.updateCompany({ ownerUserId, companyId, patch: { removeClaimIds } });
}

Type guard

function existingClaimIds(current: { claimId: string }[], requested: string[]): Set<string> {
  const known = new Set(current.map((c) => c.claimId));
  return new Set(requested.filter((id) => known.has(id)));
}

Try / catch

try {
  await api.companyMonitoring.updateCompany({ ownerUserId, companyId, patch });
} catch (err) {
  if (err instanceof ConvexError && err.data === "CLAIM_NOT_FOUND") {
    const fresh = await api.companyMonitoring.listClaims({ ownerUserId, companyId });
    const known = new Set(fresh.map((c) => c.claimId));
    await api.companyMonitoring.updateCompany({
      ownerUserId, companyId,
      patch: { ...patch, removeClaimIds: (patch.removeClaimIds ?? []).filter((id) => known.has(id)) },
    });
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Removing a claim that was already deleted in another session; stale UI state listing claims removed moments earlier; sending addClaims and removeClaimIds for the same id in one patch (the add has not landed when removal is validated); claim ids from a different company.

Common situations: Two editors editing claims concurrently; client cache behind the server; retry of an old patch after claims changed; scripts replaying recorded patches against refreshed data.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-08-21). Data as JSON: /api/errors/5654f7939986bb82. Report an issue: GitHub.

Appendix: 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 7d06c8633d)