koala73/worldmonitor · error · ConvexError
COMPANY_MONITORING_ _INVALID
Error message
COMPANY_MONITORING_${field}_INVALID What it means
Thrown by admissionIdentifier() in the company-monitoring classification admission mutation (convex/companyMonitoring/admission.ts:28). Worker-supplied identifiers are interpolated into the message, so the actual codes are COMPANY_MONITORING_ADMISSION_WORKER_ID_INVALID, COMPANY_MONITORING_ADMISSION_LEASE_INVALID, and COMPANY_MONITORING_CLASSIFICATION_RUN_ID_INVALID. An id is rejected unless it matches /^[A-Za-z0-9._:-]{1,128}$/ (non-empty, max 128 chars, restricted alphabet).
Solutions
- Sanitize the id right before the call: trim whitespace and re-encode into the allowed alphabet (e.g. base64url with + -> -, / -> _, padding stripped)
- Generate workerId/classificationRunId from the same helper the server uses (randomFence in convex/companyMonitoring/_shared.ts) so alphabets can never diverge
- If an upstream id is longer than 128 chars or uses a wider alphabet, hash it (sha256 hex) before sending
- Add a unit test asserting your worker's id generator output always matches /^[A-Za-z0-9._:-]{1,128}$/
Example fix
// before
await ctx.runMutation(internal.companyMonitoring.admission.recordClassification, {
workerId: `worker/${process.env.POD_NAME}`,
classificationRunId: `run-${crypto.randomUUID()}`,
...
});
// after
const ADMISSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
function toAdmissionId(value: string): string {
const encoded = value.trim().replace(/[^A-Za-z0-9._:-]/g, "-").slice(0, 128);
if (!ADMISSION_ID.test(encoded)) throw new Error(`unencodable id: ${value}`);
return encoded;
}
await ctx.runMutation(internal.companyMonitoring.admission.recordClassification, {
workerId: toAdmissionId(`worker-${process.env.POD_NAME}`),
classificationRunId: toAdmissionId(`run-${crypto.randomUUID()}`),
...
}); Defensive patterns
Strategy: validation
Validate before calling
const ADMISSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
function toAdmissionId(value: string, field: string): string {
const cleaned = value.trim().replace(/[^A-Za-z0-9._:-]/g, "-").slice(0, 128);
if (!ADMISSION_ID.test(cleaned)) {
throw new Error(`${field} cannot be encoded to a valid admission id: ${JSON.stringify(value)}`);
}
return cleaned;
}
// before the mutation:
const workerId = toAdmissionId(rawWorkerId, "workerId");
const leaseToken = toAdmissionId(rawLeaseToken, "leaseToken");
const classificationRunId = toAdmissionId(rawRunId, "classificationRunId"); Type guard
function isAdmissionIdentifier(value: unknown): value is string {
return typeof value === "string" && /^[A-Za-z0-9._:-]{1,128}$/.test(value);
} Try / catch
try {
await ctx.runMutation(internal.companyMonitoring.admission.recordClassification, args);
} catch (err) {
if (err instanceof ConvexError && /^COMPANY_MONITORING_.*_INVALID$/.test(String(err.data))) {
logger.error("admission argument rejected", { code: err.data, args: redactIds(args) });
return; // do not retry: fix the id generation first
}
throw err;
} Prevention
- Generate worker ids with the server's own helper (randomFence) so alphabets match by construction
- Never build ids from unvalidated external input; encode or hash first
- Assert the id regex in worker startup self-checks before any classification runs
- Keep a unit test locking your id generator to the allowed alphabet and 128-char cap
When it happens
Trigger: Calling the internal admission mutation with a workerId, leaseToken, or classificationRunId that is empty, longer than 128 characters, or contains characters outside A-Z a-z 0-9 . _ : - . Concrete hits: a run id built as `run/${uuid}` (slash), a lease token copied from a log with a trailing newline, a workerId like "worker (pod-1)" with spaces, or an undefined value coerced to the string "undefined".
Common situations: Worker code composing ids from free-form template strings or external payloads; copying lease tokens out of logs/JSON with whitespace; a dependency upgrade changing id alphabets (e.g. base64 ids containing + or =); concatenating fields until the id exceeds 128 chars.
Related errors
- COMPANY_MONITORING_EVIDENCE_REVISION_INVALID
- COMPANY_MONITORING_MODEL_VERSION_INVALID
- userId is required
- COMPANY_MONITORING_ADMISSION_EVIDENCE_INVALID
- COMPANY_MONITORING_ADMISSION_EVIDENCE_MISSING
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/d839a7dbb5727dbf.
Report an issue: GitHub.
Appendix: source
Thrown at convex/companyMonitoring/admission.ts:28
COMPANY_MONITORING_RETRY_POLICY,
COMPANY_MONITORING_SOURCE_POLICY_VERSION,
evaluateCompanyMonitoringClassifierTransportFailure,
evaluateCompanyMonitoringClassification,
} from "../../scripts/lib/company-monitoring-classification.mjs";
import { fingerprint, randomFence } from "./_shared";
import { assertValidCandidateState } from "./validators";
import {
companyMonitoringCandidateEvidenceSnapshotDigest as candidateEvidenceSnapshotDigest,
companyMonitoringEvidenceShape as evidenceShape,
} from "./admissionSnapshot";
const ADMISSION_LEASE_MS = 5 * 60 * 1000;
const ADMISSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
const ADMISSION_MODEL_VERSION = /^[^\u0000-\u001f\u007f]{1,200}$/u;
function admissionIdentifier(value: string, field: string) {
if (!ADMISSION_ID.test(value)) {
throw new ConvexError(`COMPANY_MONITORING_${field}_INVALID`);
}
return value;
}
function admissionModelVersion(value: string) {
if (
value !== value.trim() ||
!ADMISSION_MODEL_VERSION.test(value)
) {
throw new ConvexError("COMPANY_MONITORING_MODEL_VERSION_INVALID");
}
return value;
}
function canonicalValue(value: unknown): unknown {
if (Array.isArray(value)) return value.map(canonicalValue);
if (value && typeof value === "object") {
const row = value as Record<string, unknown>;View on GitHub (pinned to eeab0a219f)