srbhr/Resume-Matcher · error · ValueError

Original personalInfo is not a dict: {type(original_info).__

Error message

Original personalInfo is not a dict: {type(original_info).__name__}

What it means

JSON-008 type guard: the original resume's personalInfo must be a dict/object for the field-level immutability comparison (set of keys unioned, values normalized and compared). If it exists but is not a dict (e.g. a string, list, or number from malformed stored data), this ValueError is raised with the actual Python type name embedded, and the endpoint returns HTTP 400.

Source

Thrown at apps/backend/app/routers/resumes.py:538

def _validate_confirm_payload(
    original_data: dict[str, Any] | None,
    improved_data: dict[str, Any],
) -> None:
    if not original_data:
        logger.warning(
            "Skipping confirm payload validation; structured resume data unavailable."
        )
        return
    original_info = original_data.get("personalInfo")
    improved_info = improved_data.get("personalInfo")
    # JSON-008: Explicit null checks with clear error messages
    if original_info is None:
        raise ValueError("Original resume missing personalInfo")
    if improved_info is None:
        raise ValueError("Improved resume missing personalInfo")
    if not isinstance(original_info, dict):
        raise ValueError(
            f"Original personalInfo is not a dict: {type(original_info).__name__}"
        )
    if not isinstance(improved_info, dict):
        raise ValueError(
            f"Improved personalInfo is not a dict: {type(improved_info).__name__}"
        )
    fields = set(original_info.keys()) | set(improved_info.keys())
    mismatches = [
        field
        for field in sorted(fields)
        if _normalize_personal_info_value(original_info.get(field))
        != _normalize_personal_info_value(improved_info.get(field))
    ]
    if mismatches:
        raise ValueError(f"personalInfo fields changed: {', '.join(mismatches)}")


async def _generate_auxiliary_messages(

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Fix the stored resume via PATCH /resumes/{id} so personalInfo is a JSON object
  2. Re-upload/retry-processing the resume to regenerate well-typed processed_data
  3. Fix the producer that wrote the wrong shape (parser or migration script)

Example fix

// before: malformed stored data
"personalInfo": "Jane Doe, jane@example.com"
// after
"personalInfo": {"name": "Jane Doe", "email": "jane@example.com"}
Defensive patterns

Strategy: type-guard

Validate before calling

# client: verify shape before confirming
if not isinstance(original["processed_data"].get("personalInfo"), dict):
    raise TypeError("original personalInfo must be an object")

Type guard

def is_dict(v: object) -> bool:
    return isinstance(v, dict)

Try / catch

try:
    confirm(improved)
except ValueError as e:
    if 'personalInfo is not a dict' in str(e):
        repair_personal_info_shape(resume_id)  # PATCH the stored data

Prevention

When it happens

Trigger: POST /resumes/improve/confirm where the stored processed_data.personalInfo is a non-dict value — caused by hand-edited DB rows, a buggy earlier parser writing personalInfo as a string, or a JSON upload where personalInfo was a list.

Common situations: Direct SQLite manipulation; older schema versions where personalInfo held a flat string; test fixtures with wrong shape.

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/15074ebd30aa65dc. Report an issue: GitHub.