srbhr/Resume-Matcher · error · ValueError

Resume not found: {resume_id}

Error message

Resume not found: {resume_id}

What it means

update_resume in the backend database layer fetches a Resume row by primary key via session.get(Resume, resume_id) and raises ValueError when no row exists. It is the guard used by many write paths (upload, enhancements, cover letter updates, retries) to prevent silently updating a nonexistent record.

Source

Thrown at apps/backend/app/database.py:318

    async def get_master_resume(self) -> dict[str, Any] | None:
        """Get the master resume if exists."""
        async with self._session() as session:
            result = await session.execute(
                select(Resume).where(Resume.is_master.is_(True))
            )
            row = result.scalars().first()
            return self._resume_to_dict(row) if row else None

    async def update_resume(self, resume_id: str, updates: dict[str, Any]) -> dict[str, Any]:
        """Update resume by ID.

        Raises:
            ValueError: If resume not found.
        """
        async with self._session() as session:
            row = await session.get(Resume, resume_id)
            if row is None:
                raise ValueError(f"Resume not found: {resume_id}")
            for key, value in updates.items():
                if hasattr(row, key):
                    setattr(row, key, value)
                else:
                    logger.warning("Ignoring unknown resume field on update: %s", key)
            row.updated_at = _now()
            await session.commit()
            return self._resume_to_dict(row)

    async def delete_resume(self, resume_id: str) -> bool:
        """Delete resume by ID."""
        async with self._session() as session:
            row = await session.get(Resume, resume_id)
            if row is None:
                return False
            await session.delete(row)
            await session.commit()
            return True

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Verify the resume_id exists (SELECT or GET endpoint) before calling update_resume
  2. Return 404 to the client when this ValueError propagates instead of a 500
  3. Confirm the caller isn't passing a stale ID cached after deletion — refetch the resume list
  4. Check that ID types match (UUID vs str) so session.get isn't silently missing the row

Example fix

// before
await update_resume(session, resume_id, updates);
// after
const row = await get_resume(session, resume_id);
if (!row) return notFoundResponse(resume_id);
await update_resume(session, resume_id, updates);
Defensive patterns

Strategy: validation

Validate before calling

async def resume_exists(session, resume_id) -> bool:
    return await session.get(Resume, resume_id) is not None

# before updating:
if not await resume_exists(session, resume_id):
    raise HTTPException(status_code=404, detail=f"Resume not found: {resume_id}")

Try / catch

try:
    await update_resume(session, resume_id, updates)
except ValueError as e:
    if str(e).startswith("Resume not found"):
        raise HTTPException(status_code=404, detail=str(e)) from e
    raise

Prevention

When it happens

Trigger: Any caller of update_resume (apply_enhancements, upload_resume, update_resume_endpoint, retry_processing, update_cover_letter, apply_regenerated_items) passes a resume_id that has no matching row in the resumes table.

Common situations: Client holds a stale ID after the resume was deleted; ID passed with wrong type/format (e.g. string vs UUID mismatch in lookup); race between delete and update; user-supplied ID in an API call to a resource owned by another user.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — 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/913ef46aed7a1751. Report an issue: GitHub.