santifer/career-ops · error

Missing SCORE_SUMMARY block from model output

Error message

Missing SCORE_SUMMARY block from model output

What it means

The Gemini call succeeded and returned evaluationText, but it does not contain the '---SCORE_SUMMARY--- ... ---END_SUMMARY---' delimited block the parser regex (/---SCORE_SUMMARY---\s*([\s\S]*?)---END_SUMMARY---/) requires. The model drifted from the output contract: it ignored the delimiters, got truncated before closing the block (output token limit), declined the content, or a safety stop fired. The script logs the first 500 characters of the raw model output before throwing, which is the primary diagnostic.

Source

Thrown at batch-evaluate-gemini.mjs:253

  console.log(`\n========================================`);
  console.log(`🔄 Processing [${idx}]: ${companyHint} - ${titleHint}`);
  console.log(`🔗 URL: ${url}`);

  try {
    const jdText = await scrapeUrl(browser, url);
    if (!jdText || jdText.length < 100) {
      throw new Error('Extracted text too short (likely blocked or empty)');
    }

    console.log(`🧠 Calling Gemini (${modelName})...`);
    const evaluationText = await _evaluate(`URL: ${url}\n\n${jdText}`);

    // Parse output
    const summaryMatch = evaluationText.match(/---SCORE_SUMMARY---\s*([\s\S]*?)---END_SUMMARY---/);
    if (!summaryMatch) {
      console.error('Missing SCORE_SUMMARY block from model output:\n' + evaluationText.slice(0, 500));
      throw new Error('Missing SCORE_SUMMARY block from model output');
    }
    
    const block = summaryMatch[1];
    const extract = (key) => {
      const m = block.match(new RegExp(`^\\s*${key}:\\s*(.+)$`, 'mi'));
      return m ? m[1].trim() : 'unknown';
    };

    const company = extract('COMPANY');
    const role = extract('ROLE');
    const score = extract('SCORE');
    const archetype = extract('ARCHETYPE');
    const legitimacy = extract('LEGITIMACY');

    // Save
    mkdirSync(PATHS.reports, { recursive: true });
    mkdirSync(PATHS.trackerAdditions, { recursive: true });

View on GitHub (pinned to 60398d6549)

Solutions

  1. Inspect the logged raw output slice: a refusal/safety message means change the input; text cut mid-sentence means truncation.
  2. Re-run the single entry: transient format drift often passes on retry.
  3. If truncated, shorten the JD text sent to the model or raise the output token limit.
  4. Pin a more instruction-following model (raise spend_tier) for batch runs.
  5. If you edited the prompt, restore the exact ---SCORE_SUMMARY--- / ---END_SUMMARY--- delimiters so prompt and parser regex match again.

Example fix

// before (prompt ends loosely)
...then give your verdict.

// after (pin the contract in the prompt)
...then finish your response with exactly:
---SCORE_SUMMARY---
COMPANY: ...
ROLE: ...
SCORE: ...
ARCHETYPE: ...
---END_SUMMARY---
Defensive patterns

Strategy: retry

Try / catch

let evaluationText;
for (let attempt = 0; attempt < 2; attempt++) {
  evaluationText = await _evaluate(jd);
  if (/---SCORE_SUMMARY---[\s\S]*?---END_SUMMARY---/.test(evaluationText)) break;
  if (attempt === 1) {
    fs.writeFileSync(`batch/raw-output-${Date.now()}.txt`, evaluationText); // quarantine for manual parse
    throw new Error('Missing SCORE_SUMMARY block from model output');
  }
}

Prevention

When it happens

Trigger: A smaller/cheaper model tier ignoring delimiter formatting; output truncated by maxOutputTokens so ---END_SUMMARY--- never appears; the model refusing content it flags (personal data, suspicious JD text); the evaluation prompt edited so prompt delimiters and the parser regex no longer match; API safety settings blocking completion.

Common situations: Lowering spend_tier/model in config for cost and hitting weaker format adherence; very long JDs pushing the summary past the output limit; after hand-editing the evaluation prompt in modes/ the delimiter contract broke.

Related errors


AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20). Data as JSON: /api/errors/e1c079252c4e1e14. Report an issue: GitHub.