santifer/career-ops · error · Error
torre: invalid experience "${configured}" — must be one of:
Error message
torre: invalid experience "${configured}" — must be one of: ${[...EXPERIENCE_LEVELS].join(', ')} What it means
buildTorreQuery constructs the Torre search POST body. The API requires skill/role.experience to be one of a fixed allowlist (EXPERIENCE_LEVELS: 'potential-to-develop', '1-plus-year', '2-plus-years', '3-plus-years', '5-plus-years'); an unknown value causes a hard 500. When the entry configures an `experience` value not in that set, this error is thrown before any request is made.
Source
Thrown at providers/torre.mjs:116
/**
* Build the search body from the portal entry. Only filters proven to affect
* `total` are emitted — see the header note. Exported for tests.
*
* @param {any} entry
* @returns {object}
*/
export function buildTorreQuery(entry) {
/** @type {Record<string, unknown>} */
const body = {};
const search = typeof entry?.search === 'string' ? entry.search.trim() : '';
if (search) {
// `experience` is mandatory here — omitting it is a hard 500, so it is
// always emitted alongside `text` rather than being conditional on config.
const configured = typeof entry?.experience === 'string' ? entry.experience.trim() : '';
if (configured && !EXPERIENCE_LEVELS.has(configured)) {
throw new Error(
`torre: invalid experience "${configured}" — must be one of: ${[...EXPERIENCE_LEVELS].join(', ')}`,
);
}
body['skill/role'] = { text: search, experience: configured || DEFAULT_EXPERIENCE };
}
// Only the positive case is expressible: `{"remote":{"term":false}}` is not a
// verified filter, so a falsy remote_only sends no key at all rather than a
// filter that might be ignored while looking effective.
if (entry?.remote_only === true) body.remote = { term: true };
return body;
}
/**
* Normalize a single Torre opportunity. Exported for tests.
*
* Field mapping → the normalized Job shape:View on GitHub (pinned to 1696bec4d0)
Solutions
- Replace the entry's experience value with one of the allowed strings: potential-to-develop, 1-plus-year, 2-plus-years, 3-plus-years, 5-plus-years.
- If unsure, remove the `experience` key entirely — buildTorreQuery falls back to DEFAULT_EXPERIENCE ('1-plus-year').
- Check hyphenation/spelling against EXPERIENCE_LEVELS at the top of providers/torre.mjs (line 75).
- If a genuinely new level exists upstream, confirm it against the live API and add it to EXPERIENCE_LEVELS deliberately.
Example fix
// before (portals.yml) - name: torre-backend provider: torre search: backend engineer experience: senior // Error: invalid experience "senior" — must be one of: potential-to-develop, 1-plus-year, ... // after - name: torre-backend provider: torre search: backend engineer experience: 5-plus-years
Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED = new Set(['potential-to-develop','1-plus-year','2-plus-years','3-plus-years','5-plus-years']);
function hasValidTorreExperience(entry) {
const e = entry?.experience;
return e === undefined || e === '' || ALLOWED.has(String(e).trim());
} Type guard
function isTorreExperience(v) {
return typeof v === 'string' &&
['potential-to-develop','1-plus-year','2-plus-years','3-plus-years','5-plus-years'].includes(v);
} Try / catch
try {
await torreProvider.fetch(entry, ctx);
} catch (err) {
if (String(err.message).startsWith('torre: invalid experience')) {
console.error(`${err.message} — omit the key to use the default`);
// retry with experience removed, or fix config first
} else throw err;
} Prevention
- Copy experience values only from the documented enum, never from Torre UI labels.
- Omit the experience key when unsure — the provider defaults to '1-plus-year'.
- Validate portal configs against the enum at load time (fail before scanning starts).
- Watch for typos like '1-plus-years' or '5+ years' in hand-edited YAML.
When it happens
Trigger: A portals entry for the Torre provider sets `experience:` to an unrecognized string — e.g. 'no-experience', 'senior', '10-plus-years', '5+ years' (spaces/plus instead of hyphenated form), or a localized label — while also setting a `search` text.
Common situations: Authoring portal config from memory instead of the documented enum; copying experience values from a Torre UI label rather than its API value; a typo like '1-plus-years'; an older config written against a changed enum.
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
- Unknown template format: ${format}
- Unknown template kind: ${kind}
- Invalid page budget "${maxPages}". Use a positive integer.
- Invalid URL: ${url}
- ${key} must be an array in ${path}
AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01).
Data as JSON: /api/errors/c58453ce1942c9bb.
Report an issue: GitHub.