Skip to content

[Provider compatibility] LM Studio Chat: multiple/late system messages violate a strict model template #7761

Closed

Description

@Meistertech-JP

Client or integration

Codex App

Provider or upstream service

LM Studio local server, reached over LAN, using openai-chat (the adapter offered by the built-in LM Studio (local) preset).

Affected served model ID: qwen3.8-27b-uncensored@q4_k_m

Model file identified in the LM Studio log: JonathanColetti/Qwen3.8-27B-Uncensored-GGUF/Qwen3.8-27B-Uncensored-Q4_K_M.gguf.

OpenCodex version

Reproduced on installed @bitkyc08/opencodex 2.91.0, Windows 10.

Source inspection found the generic chronological developer mapping and the model-name-based compatibility selector in:

  • main / latest release v2.92.0, commit 9f0d40a25f8b842c33ab233d466d0bd991d86052.
  • dev, commit b8ba848cc08671a830163f361455387e55e48fa7.

Those newer revisions were inspected, not deployed or live-tested. The exact LM Studio application version was not captured.

Endpoint or capability

Codex Responses → IR → Chat translation; upstream POST /v1/chat/completions; system/developer message compatibility with the model's leading-system-only template.

This report is limited to the upstream openai-chat path used by the LM Studio preset. The client-facing Responses API is only the inbound protocol here.

Current behaviour

The model is discovered by LM Studio, but a Codex conversation fails with proxy HTTP 502 and upstream HTTP 500:

Jinja Exception: System message must be at the beginning.

The LM Studio request log shows this outbound role sequence:

system → system → system → user → system → user

The immediate failure is a mismatch between the outbound message layout and the served model's template: the template rejects multiple/late instruction messages. This is the same error class as #6242 and #6674. The model is available and accepts requests after the outbound layout is normalized; this is not a model-discovery or undefined-model error.

Source inspection explains why the existing workaround does not cover this request. In src/adapters/openai-chat/messages.ts, leadingSystemTemplate recognizes selected exact checkpoint names (with optional gateway prefixes). It matches Qwen3.8-27B and openai/Qwen3.8-27B, but not the served ID qwen3.8-27b-uncensored@q4_k_m. The generic developer-to-system mapping therefore remains in effect. This selector mismatch is a gap in the existing mitigation, not evidence that an unrecognized model name inherently causes the failure. A template that accepts the emitted roles and positions would not have this incompatibility.

No controlled test changed only the served model name, so this report does not claim that renaming alone is sufficient to reproduce or fix the issue.

Expected behaviour

The LM Studio integration should be able to serialize Codex instructions into a layout accepted by the selected model's template. For templates requiring one leading system message, an explicit compatibility policy should be available without depending solely on a list of recognized checkpoint names.

Codex's instruction content must remain present, and user messages and tool-call/result ordering must be preserved. Any loss of instruction precedence or chronological placement should be documented. Template requirements can differ between models and deployments; broadening a model-name matcher alone may not provide the right policy for each template.

Minimal redacted request or reproduction

  1. Serve qwen3.8-27b-uncensored@q4_k_m in LM Studio.
  2. Configure an OpenCodex provider using openai-chat, the default adapter of the built-in LM Studio (local) preset. Point it at the LM Studio server; this installation uses a LAN endpoint and a renamed provider (lm-studio-lan). The untouched preset name/loopback address was not separately tested.
  3. Select that served model in Codex App and send a turn with Codex developer instructions/reminders in the conversation history.
  4. Inspect the upstream Chat request: multiple/late system messages are emitted and the model rejects them.

Relevant redacted provider fields:

{
  "adapter": "openai-chat",
  "baseUrl": "http://<LM_STUDIO_HOST>:1234/v1",
  "authMode": "local"
}

The following is a reduced illustration of the observed wire shape, not a separately tested reproduction command:

{
  "model": "qwen3.8-27b-uncensored@q4_k_m",
  "messages": [
    { "role": "system", "content": "Initial instructions" },
    { "role": "user", "content": "Hello" },
    { "role": "system", "content": "Later developer reminder" },
    { "role": "user", "content": "Continue" }
  ]
}

Actual response or error

Proxy: HTTP 502
Upstream: HTTP 500
Jinja Exception: System message must be at the beginning.

Reduced error fields from the LM Studio log:

{
  "error": {
    "code": 500,
    "message": "Jinja Exception: System message must be at the beginning.",
    "type": "server_error"
  }
}

Upstream documentation

The concrete compatibility requirement here is demonstrated by the actual served model's rejection in the LM Studio logs; this report does not claim that all LM Studio models require this role layout.

Suggested mapping or implementation notes

A provider-scoped local workaround consolidates text-only system/developer instructions into one leading system message while preserving their relative text order and keeping non-instruction messages in their existing order. It operates on the outbound copy, without changing saved history.

Verification of that workaround:

  • Serialized-body checks covered translated Chat and direct Chat passthrough, instruction retention/order, user/tool preservation, provider isolation, and input immutability.
  • A minimal translated Chat request reached LM Studio and returned HTTP 200 (the deliberately small output limit ended generation with finish_reason: length).
  • After applying the workaround and restarting OpenCodex, the user confirmed normal operation in the affected Codex session and on the LM Studio host.

This workaround is not semantically identical to upstream's existing Qwen mapping: hoisting changes the chronological scope of later instructions, whereas upstream preserves their position by converting them to user, which loses developer-role precedence. Maintainer guidance on the intended tradeoff would be useful.

For an upstream fix, consider an explicit compatibility option that the LM Studio preset can expose or initialize when appropriate, with an override for templates that accept chronological instruction roles. It should continue working after a provider is renamed or its host/port changes. The local workaround's hardcoded provider name is not proposed as the upstream design.

Additional context and attachments

Relevant history:

Current selector and conversion code inspected:

const leadingSystemTemplate = !nativeOpenAI && /^(?:[^/]+\/)*(?:flash-next|Qwen3\.8-Flash-Next|Qwen3\.8-27B|qwen3-8-27b(?:-(?:fp8|lora))?|OrcaSAQ-2-Cyber-27B(?=[^:/\s]*GGUF)[^:/\s]*(?::[\w-]+)?)$/i.test(parsed.modelId);

The model worked in this environment around two months earlier. The September chronology change is consistent with the observed difference, but no historical binary bisect was performed, so this is not claimed as a proven first-bad commit.

Checks

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification or a concrete client requirement.

Activity

  1. added
    providerProvider adapters, OpenAI-compat presets, upstream API quirks
    toolstool_calls, MCP, web-search / sidecar tools
    on Oct 26, 2026
  2. Ingwannu commented on Oct 29, 2026

    @Ingwannu
    Owner

    Closing as completed on dev: #7882 merged as e3ebd82a5b51926d964e5d0e86dd0177aed9e2da, which I verified is an ancestor of current trusted dev 9b78a16f4fe0c300527e6b183052407269bb941d.

    The singleLeadingSystemMessage compatibility policy is seeded by the LM Studio preset and shared by native/translated Chat. Renamed/custom rows can opt in explicitly. Instruction hoisting changes chronological scope and cache prefixes, as the documentation explains.

    I checked the implementation and the successful exact-head required CI for 462888562c6fc593740302a8b707695abdff2117. Existing author regression receipts remain author-run evidence; I did not rerun unchanged passing suites or reproduce this on a live account/native client. This closure records the landed scope, not validation of the reporter's installed version or a release claim. If the same failure occurs on a build containing this commit, provide a new bounded, redacted reproduction against that build.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    providerProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportstoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions