santifer/career-ops · error · Error
wttj: `filters` is too long
Error message
wttj: `filters` is too long (${filters.length} > ${FILTERS_MAX_LEN} chars) What it means
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.
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
Example fix
# before wttj: filters: "(company_slug=a OR company_slug=b OR ... 200 more clauses)" # after: split across entries wttj: filters: "(company_slug=a OR company_slug=b OR company_slug=c)" queries: ["engineer"]
Defensive patterns
Strategy: validation
Validate before calling
// validate the portals.yml entry before invoking the provider
const filters = (entry?.wttj?.filters ?? '').trim();
if (filters.length > 2000) { // match FILTERS_MAX_LEN
throw new Error(`wttj.filters too long (${filters.length} chars) — split the entry`);
} Type guard
function hasValidWttjFilters(entry, max = 2000) {
const f = entry?.wttj?.filters;
return f === undefined || (typeof f === 'string' && f.trim().length <= max);
} Try / catch
try {
resolveConfig(entry);
} catch (e) {
if (e.message.startsWith('wttj: `filters` is too long')) {
console.warn('Split wttj.filters across multiple portals.yml entries.');
} else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- wttj: the WTTJ board is global — narrow it with `wttj
- apify: entry has invalid field_map. Each of title, url…
- apify: entry missing 'actor' (e.g. misceres/indeed-scraper)
- arbeitnow: invalid URL
- ashby: invalid URL
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/61ba4c3882887e4c.
Report an issue: GitHub.
Appendix: source
Thrown at providers/wttj.mjs:170
if (min || max) {
job.salary = {
min: min || max,
max: max || min,
currency: typeof h.salary_currency === 'string' ? h.salary_currency.trim().toUpperCase() : '',
};
}
return job;
}
/** Resolve config: queries and/or an Algolia filter expression, + per-query hit cap. */
function resolveConfig(entry) {
const cfg = entry?.wttj && typeof entry.wttj === 'object' ? entry.wttj : {};
const queries = Array.isArray(cfg.queries)
? cfg.queries.filter((q) => typeof q === 'string' && q.trim()).map((q) => q.trim())
: [];
const filters = typeof cfg.filters === 'string' && cfg.filters.trim() ? cfg.filters.trim() : '';
if (filters.length > FILTERS_MAX_LEN) {
throw new Error(`wttj: \`filters\` is too long (${filters.length} > ${FILTERS_MAX_LEN} chars)`);
}
if (queries.length === 0 && !filters) {
throw new Error(
'wttj: the WTTJ board is global — narrow it with `wttj: { filters: "…" }` and/or `wttj: { queries: ["…"] }`',
);
}
// A filter expression already narrows the board server-side, so the empty query
// ("match everything that passes the filter") is the useful default. Without a
// filter there is nothing to narrow the board, so a query list stays mandatory.
const effectiveQueries = queries.length > 0 ? queries : [''];
const cap = filters ? FILTERED_MAX_HITS_CAP : MAX_HITS_CAP;
const maxHits =
Number.isInteger(cfg.max_hits) && cfg.max_hits > 0 ? Math.min(cfg.max_hits, cap) : DEFAULT_MAX_HITS;
return { queries: effectiveQueries, filters, maxHits };
}
/** @type {Provider} */
export default {View on GitHub (pinned to aac998c7ed)