lfnovo/open-notebook · warning · HTTPException

Episode profile '{profile_name}' not found

Error message

Episode profile '{profile_name}' not found

What it means

HTTP 404 from GET /api/episode-profiles/{profile_name} when EpisodeProfile.get_by_name finds no profile with that exact name.

Source

Thrown at api/routers/episode_profiles.py:105

    except HTTPException:
        raise
    except OpenNotebookError:
        raise
    except Exception as e:
        logger.error(f"Failed to fetch episode profiles: {e}")
        raise HTTPException(
            status_code=500, detail="Failed to fetch episode profiles"
        )


@router.get("/episode-profiles/{profile_name}", response_model=EpisodeProfileResponse)
async def get_episode_profile(profile_name: str):
    """Get a specific episode profile by name"""
    try:
        profile = await EpisodeProfile.get_by_name(profile_name)

        if not profile:
            raise HTTPException(
                status_code=404, detail=f"Episode profile '{profile_name}' not found"
            )

        return _profile_to_response(
            profile, await _speaker_name_for(profile.speaker_config)
        )

    except HTTPException:
        raise
    except OpenNotebookError:
        raise
    except Exception as e:
        logger.error(f"Failed to fetch episode profile '{profile_name}': {e}")
        raise HTTPException(
            status_code=500, detail="Failed to fetch episode profile"
        )

View on GitHub (pinned to a7de90d38a)

Solutions

  1. GET /api/episode-profiles to see actual available names
  2. Check for exact case/whitespace/URL-encoding mismatches in the name
  3. If deleted/renamed, update the client to use the current name
  4. Avoid hardcoding profile names; look them up dynamically
Defensive patterns

Strategy: validation

Validate before calling

names = {p["name"] for p in (await client.get("/api/episode-profiles")).json()}
if profile_name not in names:
    profile_name = next(iter(names))  # or show a picker

Type guard

def profile_exists(name: str, profiles: list[dict]) -> bool:
    return any(p["name"] == name for p in profiles)

Try / catch

try:
    profile = await client.get(f"/api/episode-profiles/{quote(profile_name)}")
except httpx.HTTPStatusError as e:
    if e.response.status_code == 404:
        await refresh_profile_list()  # stale reference; reload
    else:
        raise

Prevention

When it happens

Trigger: Requesting a profile name that doesn't exist, differs by case/whitespace, or was renamed/deleted concurrently.

Common situations: Stale frontend dropdown after another user renamed/deleted a profile; URL-encoding issues turning spaces into '+' or %20 mismatches; environment differences between dev and prod databases.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27). Data as JSON: /api/errors/490882eac6071d0b. Report an issue: GitHub.