koala73/worldmonitor · error · ConvexError

INVALID_

Error message

INVALID_${field.toUpperCase()}

What it means

Dynamic validation sentinel from normalizeRequestId in the Company Monitoring shared helpers: a request-identifier field (the embedded INVALID_<FIELD> names it, e.g. INVALID_REQUESTID) failed validation — empty after trim, over length, or containing control/invisible Unicode characters matched by REQUEST_CONTROL. The input at fault is the request-correlation identifier supplied by the client.

Solutions

  1. Regenerate the request id client-side: a fresh UUID or Crockford-style id with no control or invisible characters
  2. Validate the id (non-empty, within length, no control chars) before submitting
  3. If the client builds ids from user text, sanitize or reject that copy source
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at convex/companyMonitoring/_shared.ts:24 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21). Data as JSON: /api/errors/2d9c9349e2790c15. Report an issue: GitHub.

Appendix: source

Thrown at convex/companyMonitoring/_shared.ts:24

  type NormalizedMonitoredCompanyInput,
} from "../../shared/company-monitoring-contract";

export const COMPANY_LIMIT = COMPANY_MONITORING_LIMITS.maxCompaniesPerAccount;
export const COMPANY_MONITORING_CLAIM_POLICY_VERSION = 1;
const CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
const REQUEST_CONTROL = /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u180e\u200b-\u200f\u2028-\u202e\u2060-\u206f\ufeff\ufff9-\ufffb]/u;

type CompanyMonitoringCtx = MutationCtx | QueryCtx;

export function hasCurrentCompanyMonitoringClaimPolicy(
  account: Pick<Doc<"companyMonitoringAccounts">, "claimPolicyVersion">,
): boolean {
  return (account.claimPolicyVersion ?? 0) >= COMPANY_MONITORING_CLAIM_POLICY_VERSION;
}

export function normalizeRequestId(value: string, field = "clientRequestId"): string {
  if (typeof value !== "string" || REQUEST_CONTROL.test(value)) {
    throw new ConvexError(`INVALID_${field.toUpperCase()}`);
  }
  const normalized = value.normalize("NFC").trim();
  if (!normalized || new TextEncoder().encode(normalized).byteLength > 64) {
    throw new ConvexError(`INVALID_${field.toUpperCase()}`);
  }
  return normalized;
}

export async function fingerprint(value: unknown): Promise<string> {
  const bytes = new TextEncoder().encode(JSON.stringify(value));
  const digest = await crypto.subtle.digest("SHA-256", bytes);
  return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
}

export function randomFence(): string {
  const bytes = new Uint8Array(32);
  crypto.getRandomValues(bytes);
  return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");

View on GitHub (pinned to eeab0a219f)