zylon-ai/private-gpt · warning · ValueError
Invalid system specification (dict): {system}
Error message
Invalid system specification (dict): {system} What it means
Raised by FileService._validate_file_id when any path segment of the storage path equals '..'. It is a path-traversal guard applied after decoding the file_id into a canonical storage path, rejecting ids that would escape their intended folder. FastAPI surfaces it as a 400 Bad Request.
Source
Thrown at private_gpt/chat/input_models.py:191
# If already a System instance, return as-is
if isinstance(system, System):
return system
# TextBlock -> System(text=...)
if isinstance(system, TextBlock):
return System(text=system.text)
# String -> System(text=...)
if isinstance(system, str):
return System(text=system)
# Dict -> try to convert to System via pydantic
if isinstance(system, dict):
try:
return System.model_validate(system)
except Exception as e:
raise ValueError(f"Invalid system specification (dict): {system}") from e
# List: allow list of System / str / dict and convert+merge
if isinstance(system, list):
# Convert each item to System
converted: list[System] = []
for item in system:
if isinstance(item, System):
converted.append(item)
elif isinstance(item, TextBlock):
converted.append(System(text=item.text))
elif isinstance(item, str):
converted.append(System(text=item))
elif isinstance(item, dict):
try:
converted.append(System.model_validate(item))
except Exception as e:
raise ValueError(
f"Invalid system item in list (dict): {item}"View on GitHub (pinned to 4a030776a3)
Solutions
- Remove any '..' segments from user-supplied path components before encoding them into a file_id.
- Use only ids returned by the upload/list APIs rather than constructing ids from raw filenames.
- If you are intentionally testing traversal protection, expect a 400 and move on — the guard is working as designed.
Example fix
// before
const id = encodeFileId(`uploads/${fileName}`); // fileName may contain '..'
// after
const safe = fileName.split('/').filter((s) => s && s !== '.').join('/');
if (safe.includes('..')) throw new Error('invalid filename');
const id = encodeFileId(`uploads/${safe}`); Defensive patterns
Strategy: validation
Validate before calling
function safeSegments(p) {
const segs = p.split('/');
if (segs.includes('..')) throw new Error('path traversal rejected');
return segs.filter(Boolean).join('/');
}
const fileId = encodeId(`uploads/${safeSegments(userInput)}`); Type guard
const hasNoTraversal = (s) => !s.split('/').includes('..'); Try / catch
try { await api.fileContent(scopeId, fileId); }
catch (e) { if (e.status === 400) { /* invalid id: log and reject input */ } else throw e; } Prevention
- Sanitize every path segment that feeds into a file id
- Treat 400 from this endpoint as a client bug, not a transient error
- Add fuzz tests asserting traversal ids are rejected client-side before shipping
When it happens
Trigger: Supplying a file_id that decodes to a path like 'uploads/../../etc/passwd'; fuzzing or manually crafting ids; a client bug that concatenates user input containing '..' into the id field.
Common situations: Security scanners and penetration tests probing the files API; client code building file ids from filenames without sanitization; server-side tests asserting traversal is blocked.
Related errors
- 'allOf' must be an array of schemas
- Invalid system item in list (dict): {item}
- UNSAFE_PATH_TRAVERSAL
- Handler must define function handle(input, context).
- Invalid system item in list: {item}
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/bced5e795d40f0b6.
Report an issue: GitHub.