santifer/career-ops · error

Gemini returned an invalid career-ops report

Error message

Gemini returned an invalid career-ops report: ${issues.join('; ')}

What it means

validateEvaluationShape in gemini-eval.mjs collects all structural problems found in the model's returned report (missing sections, non-numeric or out-of-range SCORE_SUMMARY score, etc.) and throws once with every issue joined by ';'. It guarantees the Gemini output conforms to the career-ops report contract before it is used downstream.

Solutions

  1. Re-run the evaluation — non-deterministic model output often passes on retry.
  2. Check the listed issue(s) and adjust the prompt/temperature to enforce the exact report template.
  3. Pin a model version whose output format is known-good, and lower temperature for structured output.
  4. Add explicit few-shot output-format instructions or a response schema if the API supports one.

Example fix

// before
const score = '4/5'; // model output, rejected
// after (prompt addition)
"SCORE_SUMMARY score MUST be a bare number between 0 and 5, e.g. `Score: 4.2`"
Defensive patterns

Strategy: try-catch

Validate before calling

function looksLikeReport(text) { return typeof text === 'string' && /SCORE_SUMMARY/i.test(text) && /Score:\s*\d+(\.\d+)?/.test(text); }

Type guard

const isValidScore = (v) => typeof v === 'number' && Number.isFinite(v) && v >= 0 && v <= 5;

Try / catch

try { validateEvaluationShape(report); } catch (e) { if (/invalid career-ops report/.test(e.message)) { retryWithReinforcedPrompt(); } else throw e; }

Prevention

When it happens

Trigger: The Gemini API response parsed but violated the expected report shape: a missing required section, SCORE_SUMMARY score absent/non-numeric/negative/above 5, or multiple rule violations accumulated in `issues`.

Common situations: Model version drift producing a re-formatted answer; a truncated response cutting off required sections; a prompt/temperature change making the model output prose instead of the structured template; a score given as '4/5' or 'good' instead of a number.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/6489361f1022f4d9. Report an issue: GitHub.

Appendix: source

Thrown at gemini-eval.mjs:285

  } else {
    const summaryBlock = summary[1];
    for (const key of ['COMPANY', 'ROLE', 'ARCHETYPE', 'LEGITIMACY']) {
      const field = summaryBlock.match(new RegExp(`^\\s*${key}:\\s*(.+)$`, 'mi'));
      const value = field?.[1]?.trim() ?? '';
      if (!value || (key !== 'COMPANY' && value.toLowerCase() === 'unknown')) {
        issues.push(`SCORE_SUMMARY ${key} is required`);
      }
    }

    const score = summaryBlock.match(/^\s*SCORE:\s*([0-9]+(?:\.[0-9]+)?)/mi);
    const scoreValue = score ? Number(score[1]) : NaN;
    if (!Number.isFinite(scoreValue) || scoreValue < 0 || scoreValue > 5) {
      issues.push('SCORE_SUMMARY score must be a number between 0 and 5');
    }
  }

  if (issues.length > 0) {
    throw new Error(`Gemini returned an invalid career-ops report: ${issues.join('; ')}`);
  }
}

function slugifyCompany(value) {
  return String(value || '')
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-|-$/g, '') || 'unknown';
}

/**
 * Whether a value is a complete http(s) URL, and so can become a dedup key.
 * @param {string} value - Candidate posting URL.
 * @returns {boolean} True only for a parseable http/https URL with a host.
 */
function isPostingUrl(value) {
  try {
    const parsed = new URL(value);

View on GitHub (pinned to aac998c7ed)