VectifyAI/PageIndex · error · PageIndexAPIError
system must be a string or a list of blocks.
Error message
system must be a string or a list of blocks.
What it means
The messages() API accepts extra_system as either a plain string or a list of Anthropic system blocks. _anthropic_system() validates this before building the request; anything else (dict, int, None-as-object, tuple) is rejected with this error.
Source
Thrown at pageindex/local_chat.py:935
def _anthropic_system(client, extra_system, block: Optional[str]) -> list[dict]:
"""System blocks: cache_control marks the stable managed prefix only
(the API allows 4 breakpoints total — the varying doc block and caller
blocks must not consume the budget); the doc block and caller system
content follow as their own blocks."""
blocks = [{"type": "text",
"text": CHAT_HEADER + "\n\n" + _base_instructions(client),
"cache_control": {"type": "ephemeral"}}]
if block:
blocks.append({"type": "text", "text": block})
if extra_system is None:
return blocks
if isinstance(extra_system, str):
if extra_system.strip():
blocks.append({"type": "text", "text": extra_system})
return blocks
if isinstance(extra_system, list):
return blocks + list(extra_system)
raise PageIndexAPIError("system must be a string or a list of blocks.")
def _cache_marks(system_blocks, messages) -> int:
"""Breakpoints already on the request. The API allows 4 total; the
top-level moving breakpoint is only added when it fits."""
blocks = list(system_blocks)
for message in messages:
content = message.get("content")
if isinstance(content, list):
blocks += [b for b in content if isinstance(b, dict)]
return sum(1 for b in blocks
if isinstance(b, dict) and b.get("cache_control"))
def _dump_block(block) -> Any:
"""A content block as a plain JSON dict, minus SDK-internal fields the
API rejects (ParsedBetaTextBlock.__api_exclude__, e.g. parsed_output)
and unset response-only defaults (exclude_unset, like the SDK's ownView on GitHub (pinned to afb5e11976)
Solutions
- Pass a plain string: extra_system="Always answer in French"
- Or a list of blocks: extra_system=[{'type': 'text', 'text': '...'}]
- If the value comes from config, validate its shape before the call
Example fix
# before
client.messages("hi", extra_system={"type": "text", "text": "Be brief"})
# after
client.messages("hi", extra_system=[{"type": "text", "text": "Be brief"}]) Defensive patterns
Strategy: type-guard
Validate before calling
if extra_system is not None and not (isinstance(extra_system, str) or isinstance(extra_system, list)):
raise TypeError('extra_system must be str or list of blocks') Type guard
def is_valid_system(v) -> bool:
return v is None or isinstance(v, str) or (isinstance(v, list) and all(isinstance(b, dict) for b in v)) Prevention
- Always wrap single blocks in a list
- Validate config-sourced system prompts before the call
When it happens
Trigger: Calling client.messages(messages, extra_system={'type': 'text', ...}) (a single block dict instead of a list), or passing a non-string/non-list value such as an int or tuple.
Common situations: Assuming a single block dict is accepted like in raw Anthropic SDK calls; passing JSON-decoded data whose shape changed; forgetting to wrap one block in [ ].
Understand the failure class
Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.
Related errors
- messages must be a non-empty string or a list of message dic
- chat must be a string or a dict.
- limit must be between 1 and 100
- offset must be non-negative
- system message content must be a string or a list of text pa
AI-assisted analysis of VectifyAI/PageIndex@afb5e11976 (2026-08-27).
Data as JSON: /api/errors/b90fabcd18f96a59.
Report an issue: GitHub.