koala73/worldmonitor · error · ValidationError
must be at most ${MAX_OPERATION_ID_LENGTH} characters
Error message
must be at most ${MAX_OPERATION_ID_LENGTH} characters What it means
Inside validateOperations, an operation id (after trimming; missing ids default to the operation's index as a string) longer than MAX_OPERATION_ID_LENGTH (64 characters) is collected as a FieldViolation on operations[index].id and the whole request fails with ValidationError. Id length and duplicates reject the batch upfront because results are correlated by id; path problems, in contrast, degrade to per-operation errors.
Source
Thrown at server/worldmonitor/batch/v1/execute-batch.ts:208
// Recursion guard: the gateway forwards the marker untouched, so a batch
// arriving with it was issued BY a batch — refuse regardless of the
// per-path nested_batch check below.
if (ctx.request.headers.has(BATCH_MARKER_HEADER)) {
throw new ApiError(400, 'Nested batch requests are not allowed', '');
}
const operations = Array.isArray(req.operations) ? req.operations : [];
if (operations.length < 1 || operations.length > MAX_BATCH_OPERATIONS) {
throw new ValidationError([{
field: 'operations',
description: `must contain between 1 and ${MAX_BATCH_OPERATIONS} operations`,
}]);
}
const origin = new URL(ctx.request.url).origin;
const { validated, violations } = validateOperations(operations, origin);
if (violations.length > 0) {
throw new ValidationError(violations);
}
const headers = buildSubRequestHeaders(ctx.request.headers);
const results = await Promise.all(
validated.map((op) => runOperation(op, headers, fetchImpl)),
);
const succeeded = results.filter((r) => r.status >= 200 && r.status < 300 && !r.error).length;
return { results, succeeded, failed: results.length - succeeded };
};
}
export const executeBatch = createExecuteBatch();
View on GitHub (pinned to eeab0a219f)
Solutions
- Keep ids at or under 64 characters — a short correlation key, not a payload
- Omit id entirely: the server defaults it to the operation's index ('0', '1', ...) which is always valid
- Hash or truncate long generated ids (e.g. crypto.randomUUID() at 36 chars fits comfortably)
Example fix
// before — id carries descriptive context
{ id: `flights-${origin}-${destination}-${JSON.stringify(filters)}`, path: '/api/aviation/v1/search-google-flights' }
// after — short unique key; parameters belong in the path/query
{ id: `flights-${i}`, path: `/api/aviation/v1/search-google-flights?origin=${origin}&destination=${destination}` } Defensive patterns
Strategy: validation
Validate before calling
const MAX_OPERATION_ID_LENGTH = 64;
function normalizeOpId(id: string | undefined, index: number): string {
const trimmed = (id ?? '').trim();
return trimmed.length > 0 && trimmed.length <= MAX_OPERATION_ID_LENGTH
? trimmed
: String(index); // fall back to the server's own index-default scheme
} Type guard
function isIdLengthViolation(body: unknown): boolean {
const v = (body as { violations?: { field?: string; description?: string }[] })?.violations;
return Array.isArray(v) && v.some((x) => x.field?.startsWith('operations[') && x.field?.endsWith('.id')
&& x.description?.includes('at most'));
} Try / catch
try {
await post('/api/batch/v1/execute', { operations });
} catch (e) {
if (e instanceof HttpError && e.status === 400 && isIdLengthViolation(e.body)) {
// shorten ids to <=64 chars (or omit them) and resend the same batch
}
} Prevention
- Treat operation ids as correlation keys, not metadata — 64 chars is ample for crypto.randomUUID()
- Cap any user-derived id with .slice(0, 64) at construction time
- Omit id when you do not need custom correlation; the server assigns stable index ids
When it happens
Trigger: Ids built as UUID-plus-description or base64-encoded payloads exceeding 64 chars; natural-language ids like 'get-all-flights-for-JFK-to-LHR-roundtrip-december'; concatenating multiple identifiers into one id string.
Common situations: Client generates ids from user-supplied labels without a length cap; ids that embed query strings or encoded context; migrating from another batch API with a larger id limit.
Related errors
- duplicate id "${id}" — results would be ambiguous
- Nested batch requests are not allowed
- must contain between 1 and ${MAX_BATCH_OPERATIONS} operation
- get_intel_timeline requires at least one of domain ("conflic
- INCOMPATIBLE_DELIVERY
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/45258db7be647709.
Report an issue: GitHub.