JuliusBrussee/caveman · error · ValueError
Configure exact POST paths and native LLM protocols
Error message
Configure exact POST paths and native LLM protocols
What it means
CavemanASGIMiddleware validates its `routes` mapping at construction. The middleware only projects exact, literal POST paths onto native LLM wire protocols (openai-chat, openai-responses, anthropic-messages); wildcards, query strings, relative paths, non-string keys, unknown protocol names, or an empty mapping are rejected. This fails fast so misconfigured routing never silently bypasses or misroutes LLM traffic at runtime.
Solutions
- Use exact literal paths starting with '/', one per native endpoint (e.g. '/v1/chat/completions', '/v1/messages'), with no '*' or '?' characters
- Use exactly one of the protocol strings: 'openai-chat', 'openai-responses', 'anthropic-messages'
- Ensure routes is a non-empty Mapping[str, Protocol] and every key is a str (not bytes or None)
- Remove any query strings from paths; query parameters are part of the scope, not the route key
Example fix
// before
middleware = CavemanASGIMiddleware(app, runtime=rt, routes={'/v1/*': 'openai-chat'}, resolve_context=ctx)
// after
middleware = CavemanASGIMiddleware(app, runtime=rt, routes={'/v1/chat/completions': 'openai-chat'}, resolve_context=ctx) Defensive patterns
Strategy: validation
Validate before calling
VALID_PROTOCOLS = {'openai-chat', 'openai-responses', 'anthropic-messages'}
assert routes, 'routes must be non-empty'
for path, protocol in routes.items():
assert isinstance(path, str) and path.startswith('/'), f'bad path: {path!r}'
assert '*' not in path and '?' not in path, f'non-exact path: {path!r}'
assert protocol in VALID_PROTOCOLS, f'bad protocol: {protocol!r}' Type guard
def is_valid_routes(routes) -> bool:
return bool(routes) and all(
isinstance(p, str) and p.startswith('/') and '*' not in p and '?' not in p
and proto in ('openai-chat', 'openai-responses', 'anthropic-messages')
for p, proto in routes.items()) Prevention
- Keep route paths as constants, never build them from user input or glob templates
- Centralize the protocol names in an enum/Literal in your config layer
- Unit-test middleware construction so config errors surface in CI, not at boot
When it happens
Trigger: Instantiating CavemanASGIMiddleware with: an empty routes dict; a path key that is not a str; a path not starting with '/'; a path containing '*' or '?' (glob/query style); or a protocol value other than exactly 'openai-chat', 'openai-responses', or 'anthropic-messages' (e.g. 'chat/completions', 'OpenAI-Chat', 'openai_chat').
Common situations: Developers copy glob route patterns from web frameworks ('/v1/*'), append query strings ('/v1/chat/completions?model=x'), typo a protocol name, use an internal enum/string from their own config layer, or pass routes=None/empty when disabling routes via feature flags.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- ASGI context resolver or request bounds are invalid
- invalid_configuration
- ASGI context must come from authenticated server state
- invalid_endpoint
- apiKey, baseURL, and agent are required
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/b8977ff3c67604a8.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/asgi.py:68
raise ValueError("non-finite JSON")
class CavemanASGIMiddleware:
"""Place inside authentication and original-content guard middleware.
resolve_context may decline by returning None; existing application routing
and auth then run unchanged. It must use authenticated server state, not
namespace/session headers. This middleware never handles an auth rejection.
"""
def __init__(self, app, *, runtime: AsyncMiddlewareRuntime,
routes: Mapping[str, Protocol],
resolve_context: Callable[[ASGIScope], ASGIContext | None | Awaitable[ASGIContext | None]],
max_body_bytes: int = 2 << 20, max_request_chunks: int = 256):
if not isinstance(runtime, AsyncMiddlewareRuntime):
raise TypeError("ASGI requires AsyncMiddlewareRuntime")
if not routes or any(not isinstance(path, str) or not path.startswith("/") or "*" in path or "?" in path
or protocol not in ("openai-chat", "openai-responses", "anthropic-messages") for path, protocol in routes.items()):
raise ValueError("Configure exact POST paths and native LLM protocols")
if not callable(resolve_context) or not 1 <= max_body_bytes <= 2 << 20 or not 1 <= max_request_chunks <= 4096:
raise ValueError("ASGI context resolver or request bounds are invalid")
self.app, self.runtime = app, runtime
self.routes, self.resolve_context = dict(routes), resolve_context
self.max_body_bytes, self.max_request_chunks = max_body_bytes, max_request_chunks
self._version_supported = matches_framework(("fastapi", "0.141", "1"), ("starlette", "1.6", "2"))
if not self._version_supported and runtime.mode != "off":
runtime.decline("unsupported_version")
async def __call__(self, scope, receive, send):
if owner.get() is not None:
return await self.app(scope, receive, send)
protocol = self.routes.get(scope.get("path"))
async def passthrough(reader, reason):
if scope.get("type") != "http" or scope.get("method") != "POST" or protocol is None:
self.runtime.report(None, reason=reason, adapter="asgi")
return await self.app(scope, reader, send)View on GitHub (pinned to 3ee70a1026)