zylon-ai/private-gpt · error · ValueError

start_on can only be used with 'last' strategy

Error message

start_on can only be used with 'last' strategy

What it means

Raised by the TrimmingMemory validator when start_on is set while trim_strategy is TrimStrategy.FIRST. start_on defines a role boundary to resume history from during LAST-strategy trimming (keep newest messages, starting from a given role); it is meaningless for FIRST strategy (keep oldest messages), so the combination is rejected at construction rather than ignored.

Source

Thrown at private_gpt/components/memory/trimming_memory.py:99

        # Validate tokenizer
        tokenizer_fn = values.get("tokenizer_fn")
        if tokenizer_fn is None:
            # TODO: Replace with a default tokenizer function
            raise ValueError("tokenizer_fn must be provided.")

        # Validate text splitter
        text_splitter = values.get("text_splitter")
        if text_splitter is None:
            values["text_splitter"] = _default_text_splitter

        # Validate strategy-specific constraints
        trim_strategy = values.get("trim_strategy", TrimStrategy.LAST)
        start_on = values.get("start_on")
        include_system = values.get("include_system", True)

        if start_on and trim_strategy == TrimStrategy.FIRST:
            raise ValueError("start_on can only be used with 'last' strategy")

        if include_system and trim_strategy == TrimStrategy.FIRST:
            raise ValueError("include_system can only be used with 'last' strategy")

        return values

    @classmethod
    def from_defaults(
        cls,
        chat_history: list[ChatMessage] | None = None,
        llm: LLM | None = None,
        chat_store: BaseChatStore | None = None,
        chat_store_key: str = DEFAULT_CHAT_STORE_KEY,
        token_limit: int | None = None,
        trim_strategy: TrimStrategy = TrimStrategy.LAST,
        include_system: bool = True,
        allow_partial: bool = False,
        start_on: MessageRole | list[MessageRole] | None = None,

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Drop start_on when using TrimStrategy.FIRST.
  2. Keep start_on only with the default LAST strategy (optionally as a role or list of roles).
  3. Add cross-field validation in your config loader so start_on and FIRST cannot be combined.

Example fix

# before
TrimmingMemory(trim_strategy=TrimStrategy.FIRST, start_on=MessageRole.USER, ...)

# after
TrimmingMemory(trim_strategy=TrimStrategy.FIRST, ...)
# or keep start_on with LAST:
TrimmingMemory(trim_strategy=TrimStrategy.LAST, start_on=MessageRole.USER, ...)
Defensive patterns

Strategy: validation

Validate before calling

if trim_strategy == 'first':
    start_on = None  # only valid with 'last'
mem = Memory.from_defaults(type='trim', trim_strategy=trim_strategy, start_on=start_on)

Prevention

When it happens

Trigger: TrimmingMemory(trim_strategy=TrimStrategy.FIRST, start_on=MessageRole.USER); passing start_on='user' together with trim_strategy='first' through config or from_defaults kwargs.

Common situations: Copy-pasting a full config block between memory instances that use different strategies; refactoring from LAST to FIRST trimming and leaving start_on in place; config schemas that allow all fields simultaneously without cross-field validation.

Related errors


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