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

  1. Remove any '..' segments from user-supplied path components before encoding them into a file_id.
  2. Use only ids returned by the upload/list APIs rather than constructing ids from raw filenames.
  3. 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

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


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