srbhr/Resume-Matcher · error · ValueError

'original' may be a list only for the reorder action

Error message

'original' may be a list only for the reorder action

What it means

ResumeChange is a Pydantic model representing one LLM-proposed resume edit. Its model validator _list_original_only_for_reorder rejects a list-valued 'original' field when action is anything other than 'reorder', because a list original for text actions would silently bypass the replace verification gate and break the invented-metrics check. The error is raised at parse/validation time with this exact message.

Source

Thrown at apps/backend/app/schemas/models.py:919

    )
    action: Literal["replace", "append", "reorder", "add_skill"]
    original: str | list[str] | None = Field(
        default=None,
        description="Current text at path — for verification. May be a list (the "
        "current items) for the reorder action; only used for text verification of "
        "replace/append, ignored otherwise.",
    )
    value: str | list[str] = Field(description="New content")
    reason: str = Field(description="Why this change helps match the JD")

    @model_validator(mode="after")
    def _list_original_only_for_reorder(self) -> "ResumeChange":
        """A list ``original`` is only meaningful for ``reorder`` (the LLM sends
        the current items). For the text actions it must stay a string/None — a
        list there would silently bypass the replace verification gate and crash
        the invented-metrics check, so reject it at parse time."""
        if isinstance(self.original, list) and self.action != "reorder":
            raise ValueError("'original' may be a list only for the reorder action")
        return self


class ImproveDiffResult(BaseModel):
    """LLM output: a list of targeted resume changes."""

    changes: list[ResumeChange] = Field(default_factory=list)
    strategy_notes: str = Field(default="")

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Check the action field of the failing ResumeChange; only 'reorder' accepts a list original
  2. If the change is a text edit, convert original to a string (or null) before constructing the model
  3. If items must be passed as a list, change action to 'reorder'
  4. Tighten the LLM prompt/examples so list originals are only emitted for reorder

Example fix

// before
ResumeChange(action="replace", original=["old text"], replacement="new text")
// after
ResumeChange(action="replace", original="old text", replacement="new text")
Defensive patterns

Strategy: validation

Validate before calling

def is_valid_change(c: dict) -> bool:
    return isinstance(c.get("original"), list) == (c.get("action") == "reorder")

Type guard

def has_list_original(c: ResumeChange) -> bool:
    return isinstance(c.original, list)

Try / catch

try:
    change = ResumeChange.model_validate(raw)
except ValidationError as e:
    logger.warning("dropping invalid change: %s", e)
    change = None

Prevention

When it happens

Trigger: Constructing or parsing a ResumeChange (e.g. from LLM JSON output via ImproveDiffResult) where original is a list (e.g. ["item1","item2"]) but action is 'replace', 'add', or 'delete' instead of 'reorder'.

Common situations: LLM emits malformed change items; prompt/schema drift causes the model to send the current items list for a text edit; code that reuses the reorder payload shape for other actions.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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