affaan-m/ECC · error · HTTPException

Inactive user

Error message

Inactive user

What it means

FastAPI dependency `get_current_active_user` runs after `get_current_user` has resolved a valid token and user. If `current_user.is_active` is `False`, it raises `HTTPException(403, "Inactive user")`. The dependency is exposed as `ActiveUserDep` and used by routes that require an active account (e.g. `GET /me`, `PATCH /{user_id}`).

Source

Thrown at skills/fastapi-patterns/SKILL.md:216

        payload = jwt.decode(token, settings.secret_key, algorithms=[settings.algorithm])
        subject = payload.get("sub")
        if subject is None:
            raise credentials_exception
        user_id = int(subject)
    except (JWTError, TypeError, ValueError):
        raise credentials_exception

    user = await db.get(User, user_id)
    if user is None:
        raise credentials_exception
    return user


async def get_current_active_user(
    current_user: Annotated[User, Depends(get_current_user)],
) -> User:
    if not current_user.is_active:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Inactive user")
    return current_user


DbDep = Annotated[AsyncSession, Depends(get_db)]
CurrentUserDep = Annotated[User, Depends(get_current_user)]
ActiveUserDep = Annotated[User, Depends(get_current_active_user)]
```

---

## Router and Endpoint Design

```python
# app/routers/users.py
from typing import Annotated
from fastapi import APIRouter, HTTPException, Query, status
from fastapi.security import OAuth2PasswordRequestForm

View on GitHub (pinned to 01e15490f0)

Solutions

  1. If you are the user: complete email verification or contact an admin to re-enable the account.
  2. If you are the admin: set `user.is_active = True` in the DB and reissue.
  3. Front-end: on 403 with this detail, route the user to an "account disabled / verify email" screen, not the generic login.
  4. If the flag is intentionally `False`-until-verified, gate only the sensitive routes; consider a separate `is_verified` flag so inactive users can still reach `POST /resend-verification`.

Example fix

# before
if not current_user.is_active:
    raise HTTPException(status_code=403, detail="Inactive user")

# after — 403 is correct; add a reason so the client can branch
if not current_user.is_active:
    raise HTTPException(
        status_code=403,
        detail={"code": "inactive_user", "message": "Account is inactive", "reason": current_user.inactive_reason},
    )
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

from fastapi import HTTPException
try:
    user = await get_current_active_user(token=token)
except HTTPException as e:
    if e.status_code == 403 and 'Inactive user' in e.detail:
        redirect('/account/inactive')
    raise

Prevention

When it happens

Trigger: A user authenticates successfully (valid JWT, user exists) but their `is_active` flag is `False` — admin-disabled, soft-deleted, or not yet email-verified. Any route using `ActiveUserDep` returns 403.

Common situations: Disabled account tries to use the API after an admin ban. Email-verification gate uses `is_active=False` until confirmation. JWT was issued before the user was deactivated (token still valid, but the dependency catches it).

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/7e1981f02acf6620. Report an issue: GitHub.