srbhr/Resume-Matcher · warning · FeaturePromptsError

missing_placeholders

missing_placeholders

Error message

Feature prompts validation error (missing_placeholders)

What it means

updateFeaturePrompts throws a FeaturePromptsError when the backend answers HTTP 422 with a structured detail whose code is 'missing_placeholders'. This means the server validated the submitted prompt templates and found required {placeholder} tokens (e.g. {job_description}, {resume}) absent from one of the prompts. Unlike the sibling errors this one carries the parsed detail object, letting the UI highlight exactly which prompt/placeholder is at fault.

Source

Thrown at apps/frontend/lib/api/config.ts:394

    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    credentials: 'include',
    body: JSON.stringify(update),
  });

  if (!res.ok) {
    // Error path: body may be absent or malformed, so we tolerate parse
    // failure. A fetch body is a one-shot stream — read it once and reuse
    // for both the 422-special-case and the generic fallback.
    const errBody = (await res.json().catch(() => ({}))) as {
      detail?: FeaturePromptsValidationError | string;
    };
    if (
      res.status === 422 &&
      typeof errBody.detail === 'object' &&
      errBody.detail?.code === 'missing_placeholders'
    ) {
      throw new FeaturePromptsError(errBody.detail);
    }
    // FastAPI can return ``detail`` as a string or a structured object.
    // Stringifying an object via the ``||`` shortcut yields "[object Object]";
    // serialize explicitly.
    let message: string;
    if (typeof errBody.detail === 'string') {
      message = errBody.detail;
    } else if (errBody.detail) {
      message = JSON.stringify(errBody.detail);
    } else {
      message = `Failed to update feature prompts (status ${res.status}).`;
    }
    throw new Error(message);
  }

  // Success path: require a valid JSON body. Swallowing parse errors here
  // would let an invalid success response be returned as FeaturePrompts
  // with undefined fields — caller code would then read .cover_letter_prompt

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Read err.detail (the FeaturePromptsError payload) to find which prompt is missing which placeholder, then re-insert the exact {placeholder} tokens in the Settings editor.
  2. Validate the prompt client-side before submitting: every required placeholder for the feature must appear in the string.
  3. If placeholders were lost via copy/paste, restore from the default prompt shown in the form and re-apply edits around the tokens.
  4. Catch FeaturePromptsError separately in the save handler and render a targeted message instead of the generic save error.

Example fix

// before
await updateFeaturePrompts({ cover_letter_prompt: 'Write a cover letter for this job.' });

// after
const prompt = 'Write a cover letter for {job_title} at {company_name} using {resume}.';
const required = ['{job_title}', '{company_name}', '{resume}'];
if (required.some((p) => !prompt.includes(p))) {
  throw new Error('Prompt is missing required placeholders: ' + required.join(', '));
}
await updateFeaturePrompts({ cover_letter_prompt: prompt });
Defensive patterns

Strategy: validation

Validate before calling

const REQUIRED: Record<string, string[]> = {
  cover_letter_prompt: ['{job_title}', '{company_name}', '{resume}'],
  cold_outreach_prompt: ['{job_title}', '{company_name}', '{resume}'],
};
function missingPlaceholders(prompt: string, required: string[]): string[] {
  return required.filter((p) => !prompt.includes(p));
}

Type guard

function isFeaturePromptsError(e: unknown): e is FeaturePromptsError {
  return e instanceof FeaturePromptsError;
}

Try / catch

try {
  await updateFeaturePrompts(update);
} catch (e) {
  if (e instanceof FeaturePromptsError) {
    highlightMissingPlaceholders(e.detail);
  } else throw e;
}

Prevention

When it happens

Trigger: Saving custom cover-letter or cold-outreach prompt text where a required placeholder was deleted or renamed — e.g. a user edits the cover_letter_prompt in Settings and removes '{job_description}' or '{company_name}', or a migration/import strips placeholders, or the frontend sends a prompt built without the tokens the backend template contract requires.

Common situations: Users hand-editing prompts in the Settings feature-prompts form and pasting text over the template placeholders; locale/translation flows that swap placeholder names; automated tests or scripts posting prompt JSON copied from an older schema.

Related errors


AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28). Data as JSON: /api/errors/68015710fe5e017f. Report an issue: GitHub.