Skip to content

Skip & Fail Helpers

fastapi-has-permissions provides helper functions and result types for explicit control over the permission check flow.

fail() -- Explicitly Deny

Call fail() inside check_permissions to immediately deny the permission with a custom message:

from fastapi import Request

from fastapi_has_permissions import Permission, fail


class HasValidToken(Permission):
    async def check_permissions(self, request: Request) -> bool:
        token = request.headers.get("Authorization")

        if token is None:
            fail("Authorization header is required")

        if not token.startswith("Bearer "):
            fail("Token must use Bearer scheme")

        return True

fail() raises a PermissionCheckFailed exception internally. The library catches it and converts it into a Failed result with the provided reason. The reason is used as the HTTP exception detail.

Note

fail() has a return type of NoReturn, so the code after it is unreachable. Your type checker will correctly understand that control flow stops at fail().

skip() -- Skip the Check

Call skip() inside check_permissions to skip the permission check entirely. A skipped permission is treated as if it was never checked:

from fastapi import Request

from fastapi_has_permissions import Permission, skip


class RequiresTokenIfPresent(Permission):
    """Only validates the token if it's provided. Skips otherwise."""

    async def check_permissions(self, request: Request) -> bool:
        if "Authorization" not in request.headers:
            skip("No token provided, skipping validation")

        return request.headers["Authorization"] == "Bearer valid-token"

skip() raises a SkipPermissionCheck exception internally. The library catches it and returns a Skipped result.

Result Types

You can also return Failed and Skipped instances directly instead of using the helper functions:

from fastapi_has_permissions import Permission
from fastapi_has_permissions._results import CheckResult, Failed, Skipped


class MyPermission(Permission):
    async def check_permissions(self, request: Request) -> CheckResult:
        token = request.headers.get("Authorization")

        if token is None:
            return Skipped(reason="No token, skipping")

        if token != "Bearer valid":
            return Failed(reason="Invalid token")

        return True

CheckResult Type

CheckResult is a type alias for bool | Skipped | Failed:

Value Meaning
True Permission granted
False Permission denied (uses default or class-level message)
Failed(reason="...") Permission denied with a specific message
Skipped(reason="...") Permission check skipped entirely

Type Guards

Use is_failed() and is_skipped() to check the result type:

from fastapi_has_permissions import is_failed, is_skipped


result = await some_permission.check_permissions()

if is_skipped(result):
    print("Permission was skipped")
elif is_failed(result):
    print(f"Permission failed: {result.reason}")

These are TypeIs guards, so your type checker will narrow the type after the check.

How Skip Interacts with Composition

Skipped permissions have special behavior when combined with & and |:

AND (&)

Scenario Result
All skip Skipped
Skip + Pass Pass
Skip + Fail Fail
# If AlwaysSkip skips, the AND result depends on the other permission
Depends(AlwaysSkip() & HasAuthorizationHeader())
# With auth header: 200 OK (skip is ignored, other passes)
# Without auth header: 403 Forbidden (skip is ignored, other fails)

OR (|)

Scenario Result
All skip Skipped
Skip + Pass Pass
Skip + Fail Fail
# If AlwaysSkip skips, the OR result depends on non-skipped permissions
Depends(AlwaysSkip() | HasAdminRole())
# With admin role: 200 OK (at least one passes)
# Without admin role: 403 Forbidden (non-skipped permission fails)

NOT (~)

Scenario Result
Skip Skipped (passthrough)
# Negating a skipped permission still skips
Depends(~AlwaysSkip())
# Always: 200 OK (skip passes through)

Warning

When all permissions in a composition are skipped, the final result is Skipped, which means the request is allowed through. Design your permissions carefully to ensure at least one non-skippable check exists when needed.