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
- Drop start_on when using TrimStrategy.FIRST.
- Keep start_on only with the default LAST strategy (optionally as a role or list of roles).
- 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
- Cross-field-validate memory config: start_on/end_on only with LAST strategy.
- When flipping strategies in config, review all boundary options (start_on, include_system) in the same change.
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
- include_system can only be used with 'last' strategy
- Unknown memory type: {type}
- Token limit must be set and greater than 0.
- Unknown condense strategy: {strategy}
- TOOL_NAME_CONFLICT
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/84776297ea36c4d8.
Report an issue: GitHub.