{"record":{"id":"0c5fe9e759f6bbf5","repo":"koala73/worldmonitor","slug":"forms-must-be-a-comma-separated-form-list-such-as","errorCode":null,"errorMessage":"forms must be a comma-separated form list such as \"8-K\" or \"10-K,10-Q\"","messagePattern":"forms must be a comma-separated form list such as \"8-K\" or \"10-K,10-Q\"","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"server/worldmonitor/intelligence/v1/search-sec-filings.ts","lineNumber":49,"sourceCode":"  const startDateValid = !req.startDate || isEdgarIsoDate(req.startDate);\n  const endDateValid = !req.endDate || isEdgarIsoDate(req.endDate);\n  const violations = [\n    ...(formsNormalized === null\n      ? [{ field: 'forms', description: 'forms must be a comma-separated form list such as \"8-K\" or \"10-K,10-Q\"' }]\n      : []),\n    ...(!startDateValid\n      ? [{ field: 'start_date', description: 'start_date must be YYYY-MM-DD' }]\n      : []),\n    ...(!endDateValid\n      ? [{ field: 'end_date', description: 'end_date must be YYYY-MM-DD' }]\n      : []),\n    ...(req.startDate && req.endDate\n      && startDateValid && endDateValid\n      && req.startDate > req.endDate\n      ? [{ field: 'start_date', description: 'start_date must not be after end_date' }]\n      : []),\n  ];\n  if (violations.length > 0) throw new ValidationError(violations);\n\n  const limit = req.limit > 0 ? Math.min(req.limit, MAX_LIMIT) : DEFAULT_LIMIT;\n\n  const result = await searchEdgarFullText({\n    query,\n    forms: formsNormalized || undefined,\n    startDate: req.startDate,\n    endDate: req.endDate,\n    size: limit,\n  });\n\n  if (!result) {\n    return { results: [], total: 0, unavailable: true, fetchedAtMs: Date.now() };\n  }\n\n  return {\n    results: result.results.slice(0, limit),\n    total: result.total,","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/server/worldmonitor/intelligence/v1/search-sec-filings.ts#L31-L67","documentation":"searchSecFilings reports this violation (field 'forms') when normalizeEdgarForms returns null, i.e. req.forms cannot be interpreted as a comma-separated EDGAR form list like \"8-K\" or \"10-K,10-Q\". It is aggregated with any date violations into one ValidationError thrown together, and — like the other filters — fails closed so a dropped filter cannot silently widen the result set.","triggerScenarios":"forms='8-K;10-Q' (semicolon separator); forms='8K' in a shape the normalizer rejects; forms='all' or a free-text value like 'annual reports'; trailing separators/garbage tokens like '10-K,,,financials'.","commonSituations":"UI multi-select joined with the wrong delimiter; users typing friendly form names; hardcoded form lists that drifted from EDGAR's form taxonomy.","solutions":["Send forms as a comma-separated list of EDGAR form codes: '8-K' or '10-K,10-Q'","Join multi-select values with ',' and validate each token is a form code before submitting","Omit forms entirely when no form filter is wanted — the filter is optional"],"exampleFix":"// before — wrong delimiter from a multi-select\nforms: selectedForms.join(';')\n\n// after — documented delimiter\nforms: selectedForms.join(',')","handlingStrategy":"validation","validationCode":"// mirror the comma-separated form-list contract\nfunction normalizeForms(forms?: string): string | null {\n  if (!forms?.trim()) return '';\n  const parts = forms.split(',').map((f) => f.trim()).filter(Boolean);\n  const ok = parts.length > 0 && parts.every((f) => /^[0-9A-Z-]+$/i.test(f));\n  return ok ? parts.join(',') : null;\n}\nconst forms = normalizeForms(rawForms);\nif (forms === null) throw new ClientError('forms must be like \"8-K\" or \"10-K,10-Q\"');","typeGuard":"function isFormsViolation(body: unknown): boolean {\n  const v = (body as { violations?: { field?: string }[] })?.violations;\n  return Array.isArray(v) && v.some((x) => x.field === 'forms');\n}","tryCatchPattern":"try {\n  await searchSecFilings({ query, forms });\n} catch (e) {\n  if (e instanceof HttpError && e.status === 400) {\n    const v = (e.body as { violations?: { field?: string }[] }).violations ?? [];\n    // forms violations may arrive batched with date violations — fix all before resending\n    if (v.some((x) => x.field === 'forms')) return fixFormsInput();\n  }\n  throw e;\n}","preventionTips":["Join multi-select form filters with ',' — the normalizer rejects other delimiters","Validate each token is an EDGAR form code before submitting","Remember filters are fail-closed: a malformed filter is rejected, never silently widened"],"tags":["intelligence","sec","edgar","validation","filter-format"],"backgroundTag":"invalid-parameter-format","analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}