koala73/worldmonitor · error · ValidationError

Unsupported arXiv category

Error message

Unsupported arXiv category

What it means

listArxivPapers accepts an optional `category` filter and only serves categories the platform actively tracks. If the caller passes a category string that is not in the trackedCategories list, a ValidationError is thrown with field 'category'. This keeps cache seed keys (`arxiv:<category>`) bounded to known snapshots instead of doing uncached upstream arXiv fetches.

Solutions

  1. List the supported categories by calling the endpoint with no category (it returns all trackedCategories) and pick one from the response
  2. Fix the category string to match trackedCategories exactly, including casing and subject prefix (e.g. 'cs.LG', 'econ.EM')
  3. Trim client input before sending; whitespace or empty-string handling differs from omission
  4. If a needed category is genuinely missing, request it be added to trackedCategories rather than working around the error

Example fix

// before
await client.listArxivPapers({ category: 'cs.ml' });
// after
const all = await client.listArxivPapers({});
const category = all.papers[0]?.category ?? 'cs.LG'; // use a tracked category
await client.listArxivPapers({ category });
Defensive patterns

Strategy: validation

Validate before calling

const TRACKED = ['cs.LG','cs.AI','econ.EM']; // obtain from the unfiltered response
if (category !== undefined && category !== '' && !TRACKED.includes(category)) {
  throw new Error(`Unsupported arXiv category: ${category}`);
}

Type guard

function isTrackedCategory(c: string): boolean {
  return trackedCategories.includes(c);
}

Try / catch

try {
  return await client.listArxivPapers({ category });
} catch (e) {
  if (e instanceof ValidationError && e.fields?.[0]?.field === 'category') {
    return client.listArxivPapers({}); // fall back to all tracked categories
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling listArxivPapers with req.category set to a misspelled, renamed, or untracked arXiv category (e.g. 'cs.AI ' with whitespace, 'quant-ph' if not tracked, or an empty-but-truthy garbage string). Category must exactly match a member of trackedCategories.

Common situations: Developers guess arXiv taxonomy strings instead of copying them from the tracked list; a category was retired or renamed server-side; a client stores a stale category from an older API version; casing differs (arXiv categories are case-sensitive like 'cs.LG').

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/a45cafa262fbfb0f. Report an issue: GitHub.

Appendix: source

Thrown at server/worldmonitor/research/v1/list-arxiv-papers.ts:26

  ListArxivPapersRequest,
  ListArxivPapersResponse,
} from '../../../../src/generated/server/worldmonitor/research/v1/service_server';

import { ValidationError } from '../../../../src/generated/server/worldmonitor/research/v1/service_server';
import trackedCategories from '../../../../scripts/shared/research-arxiv-categories.json';
import { clampInt } from '../../../_shared/constants';
import { getCachedJson } from '../../../_shared/redis';
import { markNoStoreFallbackResponse } from '../../../_shared/response-headers';

const SEED_KEY_PREFIX = 'research:arxiv:v1';

export async function listArxivPapers(
  ctx: ServerContext,
  req: ListArxivPapersRequest,
): Promise<ListArxivPapersResponse> {
  const category = req.category || '';
  if (category && !trackedCategories.includes(category)) {
    throw new ValidationError([{ field: 'category', description: 'Unsupported arXiv category' }]);
  }
  const categories = category ? [category] : trackedCategories;
  const pageSize = clampInt(req.pageSize, 50, 1, 100);
  const snapshots = await Promise.all(categories.map(async (selected) => {
    try {
      return await getCachedJson(`${SEED_KEY_PREFIX}:${selected}::50`, true) as ListArxivPapersResponse | null;
    } catch {
      return null;
    }
  }));
  const papers = new Map<string, ListArxivPapersResponse['papers'][number]>();
  let incomplete = false;
  for (const snapshot of snapshots) {
    if (!Array.isArray(snapshot?.papers)) {
      incomplete = true;
      continue;
    }
    for (const paper of snapshot.papers) {

View on GitHub (pinned to 7d06c8633d)