{"record":{"id":"61ba4c3882887e4c","repo":"santifer/career-ops","slug":"wttj-filters-is-too-long-filters-length-filters-max-len","errorCode":null,"errorMessage":"wttj: `filters` is too long (${filters.length} > ${FILTERS_MAX_LEN} chars)","messagePattern":"wttj: `filters` is too long \\((.+?) > (.+?) chars\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"providers/wttj.mjs","lineNumber":170,"sourceCode":"  if (min || max) {\n    job.salary = {\n      min: min || max,\n      max: max || min,\n      currency: typeof h.salary_currency === 'string' ? h.salary_currency.trim().toUpperCase() : '',\n    };\n  }\n  return job;\n}\n\n/** Resolve config: queries and/or an Algolia filter expression, + per-query hit cap. */\nfunction resolveConfig(entry) {\n  const cfg = entry?.wttj && typeof entry.wttj === 'object' ? entry.wttj : {};\n  const queries = Array.isArray(cfg.queries)\n    ? cfg.queries.filter((q) => typeof q === 'string' && q.trim()).map((q) => q.trim())\n    : [];\n  const filters = typeof cfg.filters === 'string' && cfg.filters.trim() ? cfg.filters.trim() : '';\n  if (filters.length > FILTERS_MAX_LEN) {\n    throw new Error(`wttj: \\`filters\\` is too long (${filters.length} > ${FILTERS_MAX_LEN} chars)`);\n  }\n  if (queries.length === 0 && !filters) {\n    throw new Error(\n      'wttj: the WTTJ board is global — narrow it with `wttj: { filters: \"…\" }` and/or `wttj: { queries: [\"…\"] }`',\n    );\n  }\n  // A filter expression already narrows the board server-side, so the empty query\n  // (\"match everything that passes the filter\") is the useful default. Without a\n  // filter there is nothing to narrow the board, so a query list stays mandatory.\n  const effectiveQueries = queries.length > 0 ? queries : [''];\n  const cap = filters ? FILTERED_MAX_HITS_CAP : MAX_HITS_CAP;\n  const maxHits =\n    Number.isInteger(cfg.max_hits) && cfg.max_hits > 0 ? Math.min(cfg.max_hits, cap) : DEFAULT_MAX_HITS;\n  return { queries: effectiveQueries, filters, maxHits };\n}\n\n/** @type {Provider} */\nexport default {","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/providers/wttj.mjs#L152-L188","documentation":"resolveConfig reads the portals.yml entry's optional wttj block and rejects a `filters` string longer than FILTERS_MAX_LEN characters before it is ever sent to Algolia. This is an upfront config sanity check so an over-long filter expression is caught at configuration time rather than as an HTTP 413/400 from the Algolia API. The message reports the actual and maximum lengths.","triggerScenarios":"A portals.yml entry sets wttj.filters to a string whose trimmed length exceeds FILTERS_MAX_LEN — typically a very long hand-written filter expression or one concatenating many company/job filters.","commonSituations":"Config authors paste an enormous filter built for another board; generated filter lists accumulate too many clauses; a copy-paste duplicates the expression and blows past the cap.","solutions":["Shorten the wttj.filters expression in portals.yml below FILTERS_MAX_LEN (check the constant's value in providers/wttj.mjs)","Split the entry into multiple portals.yml entries, each with a narrower filters expression","Move some narrowing into wttj.queries (search terms) instead of server-side filters","Generate the filters programmatically and deduplicate clauses before writing the config"],"exampleFix":"# before\nwttj:\n  filters: \"(company_slug=a OR company_slug=b OR ... 200 more clauses)\"\n# after: split across entries\nwttj:\n  filters: \"(company_slug=a OR company_slug=b OR company_slug=c)\"\n  queries: [\"engineer\"]","handlingStrategy":"validation","validationCode":"// validate the portals.yml entry before invoking the provider\nconst filters = (entry?.wttj?.filters ?? '').trim();\nif (filters.length > 2000) { // match FILTERS_MAX_LEN\n  throw new Error(`wttj.filters too long (${filters.length} chars) — split the entry`);\n}","typeGuard":"function hasValidWttjFilters(entry, max = 2000) {\n  const f = entry?.wttj?.filters;\n  return f === undefined || (typeof f === 'string' && f.trim().length <= max);\n}","tryCatchPattern":"try {\n  resolveConfig(entry);\n} catch (e) {\n  if (e.message.startsWith('wttj: `filters` is too long')) {\n    console.warn('Split wttj.filters across multiple portals.yml entries.');\n  } else throw e;\n}","preventionTips":["Keep filter expressions minimal — narrow by one or two slugs, not hundreds of OR clauses","Generate filters programmatically and enforce the length cap at generation time","Review wttj entries in code review whenever FILTERS_MAX_LEN or provider logic changes","Prefer queries for title narrowing and filters only for structural constraints"],"tags":["config","validation","wttj","portals-yml"],"backgroundTag":"invalid-config-value","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}