zylon-ai/private-gpt · warning · ValueError

'allOf' must be an array of schemas

Error message

'allOf' must be an array of schemas

What it means

Pydantic field-validator error on SkillFile.path when any '/'-separated segment equals '..' — the classic traversal pattern. It prevents skill file entries from escaping the skill root via relative traversal; FastAPI surfaces it as 422.

Source

Thrown at private_gpt/chat/schema_models.py:109

    # Handle combinators first
    if "oneOf" in schema:
        if not isinstance(schema["oneOf"], list):
            raise ValueError("'oneOf' must be an array of schemas")
        for sub_schema in schema["oneOf"]:
            _validate_json_schema_item(sub_schema, strict=False)
        return  # oneOf schemas don't need type field

    if "anyOf" in schema:
        if not isinstance(schema["anyOf"], list):
            raise ValueError("'anyOf' must be an array of schemas")
        for sub_schema in schema["anyOf"]:
            _validate_json_schema_item(sub_schema, strict=False)
        return  # anyOf schemas don't need type field

    if "allOf" in schema:
        if not isinstance(schema["allOf"], list):
            raise ValueError("'allOf' must be an array of schemas")
        for sub_schema in schema["allOf"]:
            _validate_json_schema_item(sub_schema, strict=False)
        return  # allOf schemas don't need type field

    # Regular schema validation
    if "type" not in schema:
        if strict:
            raise ValueError("Schema must define a 'type' field")
        else:
            return  # Non-strict mode allows missing type

    if schema["type"] == "array" and "items" not in schema:
        raise ValueError("Array schemas must define 'items'")

    if schema["type"] == "array" and not isinstance(schema.get("items"), dict):
        raise ValueError("Array 'items' must be a dictionary representing JSON Schema")

    # Recursively validate array items

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Sanitize: split on '/', drop '', '.' and '..' segments, rejoin with '/'.
  2. Reject input containing '..' before it reaches the API call.
  3. Use ids/names produced by your own listing flow instead of raw user strings.

Example fix

# before
path = f"{user_dir}/{user_input}"  # user_input = '../../etc/passwd'

# after
parts = [s for s in f"{user_dir}/{user_input}".split('/') if s not in ('', '.', '..')]
if '..' in f"{user_dir}/{user_input}".split('/'):
    raise ValueError('invalid path')
path = '/'.join(parts)
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeSkillPath(raw) {
  if (raw.split('/').includes('..')) throw new Error('traversal rejected');
  return raw.split('/').filter((s) => s && s !== '.').join('/');
}

Type guard

const hasNoDotDot = (p) => typeof p === 'string' && !p.split('/').includes('..');

Prevention

When it happens

Trigger: Submitting path 'assets/../../SKILL.md' or any segment equal to '..'; naive concatenation of user input into the path field; security probing of the skills upload API.

Common situations: Client code joining a base directory with untrusted relative input; porting archives containing '..' entries; penetration tests expecting traversal to be blocked.

Related errors


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