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

  1. 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.
  2. If unsure, remove the `experience` key entirely — buildTorreQuery falls back to DEFAULT_EXPERIENCE ('1-plus-year').
  3. Check hyphenation/spelling against EXPERIENCE_LEVELS at the top of providers/torre.mjs (line 75).
  4. 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

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


AI-assisted analysis of santifer/career-ops@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/c58453ce1942c9bb. Report an issue: GitHub.