zylon-ai/private-gpt · error · RuntimeError
System messages should be as layer in the context stack.
Error message
System messages should be as layer in the context stack.
What it means
A plain RuntimeError raised when a message with role=system is found inside request.messages. The architecture requires system prompts to be supplied as a context-stack layer (context.state.input.context_stack.to_system_prompt()) or via request.system.get_prompt(), never as an inline system message. Hitting it means the caller is using the API incorrectly.
Source
Thrown at private_gpt/server/chat/interceptors/validator_request_interceptor.py:128
tokenize = context.state.runtime.tokenizer_fn
if token_limit is None or tokenize is None:
return
user_message_tokens = len(
await async_tokenizer(texts=user_text, tokenizer_fn=tokenize)
)
if user_message_tokens > token_limit:
raise Errors.RequestTooLarge(
f"The message length {user_message_tokens} exceeds the maximum token limit {token_limit}.",
Errors.Codes.REQUEST_TOO_LARGE_USER_MSG,
)
# If a system message is present in the request messages, it's a misuse
potential_system_message = self._system_message_text(
context.state.input.request.messages
)
if potential_system_message:
raise RuntimeError(
"System messages should be as layer in the context stack."
)
# Prefer system prompt from the context stack, fall back to prompt
system_prompt_block = (
context.state.input.context_stack.to_system_prompt()
or request.system.get_prompt()
or None
)
system_prompt = (
"\n".join(
[block.text for block in system_prompt_block]
if system_prompt_block
else []
)
if system_prompt_block
else None
)View on GitHub (pinned to 4a030776a3)
Solutions
- Remove the system message from request.messages and pass the prompt via the context stack (system prompt layer) or the request.system field.
- If using an OpenAI-compatible facade, filter out system-role messages before forwarding.
- If you intended the text as instructions, move it into the dedicated system prompt configuration.
Example fix
# before messages = [ChatMessage(role=MessageRole.SYSTEM, ...), ChatMessage(role=MessageRole.USER, ...)] # after messages = [ChatMessage(role=MessageRole.USER, ...)] request = ChatRequest(messages=messages, system=SystemPrompt(prompt="You are a helpful assistant"))
Defensive patterns
Strategy: validation
Validate before calling
assert not any(m.role == MessageRole.SYSTEM for m in request.messages), 'system role not allowed in messages'
Type guard
def has_inline_system_message(messages: list[ChatMessage]) -> bool:
return any(m.role == MessageRole.SYSTEM for m in messages) Prevention
- Never port OpenAI payloads verbatim; strip system roles first
- Put instructions in request.system or the context stack
- Add a client-side lint that rejects system-role messages
When it happens
Trigger: POSTing a messages array that contains {"role": "system", "content": ...} with non-empty text; porting OpenAI-style request payloads unchanged into this server.
Common situations: Migrating an OpenAI client integration that prepends a system message; SDK examples copied from generic chat APIs; agents/frameworks (LangChain, autogen) that auto-inject system roles into messages.
Related errors
- No user message found in request.
- Duplicate tool use ID found: {block.id}
- Tool result block references an unknown tool use ID: {block.
- TLDR blocks can only be used in assistant messages: {message
- Tool result blocks must match the tool use IDs in the same m
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/9d9a31c17e7fda27.
Report an issue: GitHub.