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

  1. Keep ids at or under 64 characters — a short correlation key, not a payload
  2. Omit id entirely: the server defaults it to the operation's index ('0', '1', ...) which is always valid
  3. 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

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


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