jumpserver/jumpserver · error · ValidationError

Expected {expected}.

Error message

Expected {expected}.

What it means

ValidationError raised when a scalar's Python type does not match any of the schema's declared types (with special handling: booleans are not accepted as integers/numbers, and numeric strings are not coerced). The message echoes the expected type(s).

Source

Thrown at apps/chat_ai/executor/request_builder.py:25

def _validate_scalar(value, schema, location):
    expected = schema.get('type')
    nullable = schema.get('nullable') or 'null' in (schema.get('type') if isinstance(schema.get('type'), list) else [])
    if value is None:
        if nullable or not expected:
            return
        raise ValidationError({location: 'This value may not be null.'})
    expected_types = expected if isinstance(expected, list) else [expected]

    def matches(item):
        if item == 'integer':
            return isinstance(value, int) and not isinstance(value, bool)
        if item == 'number':
            return isinstance(value, (int, float)) and not isinstance(value, bool)
        type_map = {'string': str, 'boolean': bool, 'array': list, 'object': dict}
        return item in type_map and isinstance(value, type_map[item])

    if expected and not any(matches(item) for item in expected_types):
        raise ValidationError({location: f'Expected {expected}.'})
    if schema.get('enum') and value not in schema['enum']:
        raise ValidationError({location: f'Value must be one of {schema["enum"]}.'})
    if isinstance(value, str):
        if schema.get('minLength') is not None and len(value) < schema['minLength']:
            raise ValidationError({location: f'Minimum length is {schema["minLength"]}.'})
        if schema.get('maxLength') is not None and len(value) > schema['maxLength']:
            raise ValidationError({location: f'Maximum length is {schema["maxLength"]}.'})
        if schema.get('pattern') and not re.search(schema['pattern'], value):
            raise ValidationError({location: 'Value does not match the required pattern.'})
    if isinstance(value, (int, float)) and not isinstance(value, bool):
        if schema.get('minimum') is not None and value < schema['minimum']:
            raise ValidationError({location: f'Minimum value is {schema["minimum"]}.'})
        if schema.get('maximum') is not None and value > schema['maximum']:
            raise ValidationError({location: f'Maximum value is {schema["maximum"]}.'})


def validate_json(value, schema, location='body'):
    if not schema:

View on GitHub (pinned to 6ec464fabd)

Solutions

  1. Coerce arguments to the schema-declared types before calling build/execute (int(x) for integer fields, etc.)
  2. Align the tool schema advertised to the agent with the Core API schema so the model emits correct types
  3. Validate arguments against the schema and retry with a corrective prompt on type errors
  4. Watch the bool special case: booleans fail integer/number checks by design

Example fix

# before
arguments = {'body': {'count': '10', 'verbose': 'true'}}

# after
arguments = {'body': {'count': int('10'), 'verbose': True}}
Defensive patterns

Strategy: validation

Validate before calling

function coerce(value, schema) {
  if (schema.type === 'integer' || schema.type === 'number') return Number(value);
  if (schema.type === 'boolean') return Boolean(value);
  return value;
}
args.body = mapSchema(args.body, bodySchema, coerce);

Type guard

const matchesType = (v: unknown, t: string): boolean =>
  t === 'integer' ? (typeof v === 'number' && Number.isInteger(v))
  : t === 'number' ? typeof v === 'number'
  : t === 'string' ? typeof v === 'string'
  : t === 'boolean' ? typeof v === 'boolean'
  : t === 'array' ? Array.isArray(v)
  : t === 'object' ? typeof v === 'object' && v !== null && !Array.isArray(v)
  : false;

Try / catch

try {
  await executor.execute(opId, args, ctx, { agentRun: run });
} catch (e) {
  if (e instanceof ValidationError && /Expected /.test(JSON.stringify(e.detail))) {
    args = coerceToSchema(args, schema);
    return await executor.execute(opId, args, ctx, { agentRun: run });
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing '5' (string) where {'type': 'integer'} is expected, true where string is expected, or 1.0 passed as an int-only field; schema declaring a list of allowed types with the value matching none.

Common situations: LLM tool-call arguments arriving as strings from JSON, frontend form inputs unconverted, schema tightened from ['integer','string'] to 'integer'.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of jumpserver/jumpserver@6ec464fabd (2026-08-28). Data as JSON: /api/errors/a694695f1bc72fee. Report an issue: GitHub.