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

  1. Remove the system message from request.messages and pass the prompt via the context stack (system prompt layer) or the request.system field.
  2. If using an OpenAI-compatible facade, filter out system-role messages before forwarding.
  3. 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

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


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/9d9a31c17e7fda27. Report an issue: GitHub.