Skip to content

[BUG] A stock SecurityConfig returns 500 to every request when Redis is not running #76

Closed

Description

@RomanPszonka

Bug description

SecurityConfig() defaults to enable_redis=True with
redis_url="redis://localhost:6379". With nothing listening there,
GuardRedisError escapes dispatch and every request — the first and all after
it — gets HTTP 500. redis_fail_open and fail_secure are not consulted on the
initialization path, so neither of the knobs that look like they cover this does.

Steps to reproduce

With nothing listening on the configured Redis port:

from fastapi import FastAPI
from fastapi.testclient import TestClient
from guard import SecurityConfig, SecurityMiddleware

app = FastAPI()
app.add_middleware(SecurityMiddleware, config=SecurityConfig())

@app.get("/")
async def root():
    return {"ok": True}

print(TestClient(app, raise_server_exceptions=False).get("/").status_code)
enable_redis=True status=500 body='Internal Server Error'
[guard_core.handlers.redis] ERROR - Redis connection failed: Error 61
connecting to 127.0.0.1:6399

Expected behaviour

Either enable_redis=False by default, or a failed connection degrades to
in-memory with a loud warning. Redis is an optional backend everywhere else in
the library; the default configuration should not make it a hard startup
dependency.

Environment

guard-core 3.13.0, fastapi-guard 7.6.0, Python 3.14, macOS 14.

Activity

  1. rennf93 commented on Aug 25, 2026

    @rennf93
    Member

    Thanks @RomanPszonka for opening the issue! By @rennf93's directive:

    Reproduced on guard-core 3.13.0 with fastapi-guard 7.7.0 (the 7.6.0 in your environment line changes nothing here; guard/middleware.py is identical between the two tags).

    Confirmed that none of redis_fail_open, fail_secure or lazy_init is consulted on the init path, in any combination: HandlerInitializer.initialize_redis_handlers awaits RedisManager.initialize() unguarded, and the only place that reads redis_fail_open is the pipeline's per-check error handler at request time. This predates 3.13.0 (identical in 3.12.0).

    Plan for 3.14.0: catch GuardRedisError at that one choke point. With redis_fail_open=True the middleware degrades to the in-memory backends with an error log and keeps retrying the connection on later requests. With the default it stays fail-closed, but the adapter turns it into a proper 503 instead of an unhandled 500. We are not flipping enable_redis to False: a security library defaulting to fail-closed when its configured backend is missing is the right default; the bug is that it did so as a crash rather than as a decision.

  2. self-assigned this
    on Aug 25, 2026
  3. added
    bugConfirmed defect
    area: core-subsystemsTouches guard_core/core/{responses,routing,validation,bypass,behavioral,events,initialization}/
    area: handlersTouches guard_core/handlers/
    on Aug 26, 2026
  4. linked a pull request that will close this issuerelease(v3.14.0): post-3.13.0 hardening #82on Aug 26, 2026
  5. rennf93 commented on Aug 26, 2026

    @rennf93
    Member

    Shipped in guard-core 3.14.0 (PyPI), merged in #82 (500d4ef).

    A Redis outage at initialization no longer crashes the app: with redis_fail_open=True guard-core degrades to the in-memory backends for the process lifetime, with redis_fail_open=False (default) it raises GuardRedisError for the adapter to render; fastapi-guard 7.8.0 answers that with 503 and Retry-After (Guard-Core/fastapi-guard#121, releasing next). The eager Redis reads in sus_patterns_handler.initialize_redis and IPInfoManager.initialize are best-effort as well, so a blip after a good connect no longer escapes the degrade.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area: core-subsystemsTouches guard_core/core/{responses,routing,validation,bypass,behavioral,events,initialization}/area: handlersTouches guard_core/handlers/bugConfirmed defect

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions