zylon-ai/private-gpt · error · ValueError

Invalid message order: expected {expected_roles} after {prev

Error message

Invalid message order: expected {expected_roles} after {previous_role}, but got {current_role}.Check ToolUseBlock and ToolResultBlock order.

What it means

Raised by the DELETE /skills/{skill_id} route when the skill does not exist in the given collection (get_skill returned None). Deletion requires the skill to be resolvable first; a miss short-circuits to 404 before any mutation is attempted.

Source

Thrown at private_gpt/chat/input_models.py:531

                    expected_roles = {
                        MessageRole.ASSISTANT,
                        MessageRole.USER,
                    }
                elif previous_role == MessageRole.ASSISTANT:
                    expected_roles = {
                        MessageRole.USER,
                        MessageRole.TOOL,
                    }
                elif previous_role == MessageRole.TOOL:
                    expected_roles = {
                        MessageRole.ASSISTANT,
                        # Mistral doesn't support user after tool
                        # Fixed in the tokenizer
                        MessageRole.USER,
                    }

                if current_role not in expected_roles:
                    raise ValueError(
                        f"Invalid message order: expected {expected_roles} after {previous_role}, but got {current_role}."
                        "Check ToolUseBlock and ToolResultBlock order."
                        if current_role == MessageRole.TOOL
                        else ""
                    )

            previous_role = current_role

    @classmethod
    def _merge_messages(cls, messages: list["MessageInput"]) -> list["MessageInput"]:
        """Merge consecutive messages with the same role."""
        if not messages:
            return []

        merged_messages: list[MessageInput] = []
        current_message = messages[0]

        for msg in messages[1:]:

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Treat 404 on delete as already-deleted and refresh the list.
  2. Confirm the collection parameter matches where the skill was created.
  3. Disable duplicate delete actions while a request is in flight.

Example fix

// before
await skillsApi.delete(id, collection); // second call 404s

// after
try { await skillsApi.delete(id, collection); }
catch (e) { if (e.status !== 404) throw e; } // 404 = already gone
Defensive patterns

Strategy: try-catch

Validate before calling

const exists = (await skillsApi.list(collection)).data.some((s) => s.id === id);
if (!exists) return; // nothing to delete

Try / catch

try { await skillsApi.delete(id, collection); }
catch (e) { if (e.status === 404) return; /* already deleted */ throw e; }

Prevention

When it happens

Trigger: DELETE /skills/{id} with an id from another collection, a deleted skill, or after the collection was cleared; duplicate delete requests.

Common situations: Two clients racing to delete the same skill; stale UI state; deleting in the wrong collection context.

Related errors


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