{"record":{"id":"a694695f1bc72fee","repo":"jumpserver/jumpserver","slug":"expected-expected","errorCode":null,"errorMessage":"Expected {expected}.","messagePattern":"Expected (.+?)\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"apps/chat_ai/executor/request_builder.py","lineNumber":25,"sourceCode":"def _validate_scalar(value, schema, location):\n    expected = schema.get('type')\n    nullable = schema.get('nullable') or 'null' in (schema.get('type') if isinstance(schema.get('type'), list) else [])\n    if value is None:\n        if nullable or not expected:\n            return\n        raise ValidationError({location: 'This value may not be null.'})\n    expected_types = expected if isinstance(expected, list) else [expected]\n\n    def matches(item):\n        if item == 'integer':\n            return isinstance(value, int) and not isinstance(value, bool)\n        if item == 'number':\n            return isinstance(value, (int, float)) and not isinstance(value, bool)\n        type_map = {'string': str, 'boolean': bool, 'array': list, 'object': dict}\n        return item in type_map and isinstance(value, type_map[item])\n\n    if expected and not any(matches(item) for item in expected_types):\n        raise ValidationError({location: f'Expected {expected}.'})\n    if schema.get('enum') and value not in schema['enum']:\n        raise ValidationError({location: f'Value must be one of {schema[\"enum\"]}.'})\n    if isinstance(value, str):\n        if schema.get('minLength') is not None and len(value) < schema['minLength']:\n            raise ValidationError({location: f'Minimum length is {schema[\"minLength\"]}.'})\n        if schema.get('maxLength') is not None and len(value) > schema['maxLength']:\n            raise ValidationError({location: f'Maximum length is {schema[\"maxLength\"]}.'})\n        if schema.get('pattern') and not re.search(schema['pattern'], value):\n            raise ValidationError({location: 'Value does not match the required pattern.'})\n    if isinstance(value, (int, float)) and not isinstance(value, bool):\n        if schema.get('minimum') is not None and value < schema['minimum']:\n            raise ValidationError({location: f'Minimum value is {schema[\"minimum\"]}.'})\n        if schema.get('maximum') is not None and value > schema['maximum']:\n            raise ValidationError({location: f'Maximum value is {schema[\"maximum\"]}.'})\n\n\ndef validate_json(value, schema, location='body'):\n    if not schema:","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/jumpserver/jumpserver/blob/6ec464fabd61b95912d539455a3a5f15f5c59fe0/apps/chat_ai/executor/request_builder.py#L7-L43","documentation":"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).","triggerScenarios":"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.","commonSituations":"LLM tool-call arguments arriving as strings from JSON, frontend form inputs unconverted, schema tightened from ['integer','string'] to 'integer'.","solutions":["Coerce arguments to the schema-declared types before calling build/execute (int(x) for integer fields, etc.)","Align the tool schema advertised to the agent with the Core API schema so the model emits correct types","Validate arguments against the schema and retry with a corrective prompt on type errors","Watch the bool special case: booleans fail integer/number checks by design"],"exampleFix":"# before\narguments = {'body': {'count': '10', 'verbose': 'true'}}\n\n# after\narguments = {'body': {'count': int('10'), 'verbose': True}}","handlingStrategy":"validation","validationCode":"function coerce(value, schema) {\n  if (schema.type === 'integer' || schema.type === 'number') return Number(value);\n  if (schema.type === 'boolean') return Boolean(value);\n  return value;\n}\nargs.body = mapSchema(args.body, bodySchema, coerce);","typeGuard":"const matchesType = (v: unknown, t: string): boolean =>\n  t === 'integer' ? (typeof v === 'number' && Number.isInteger(v))\n  : t === 'number' ? typeof v === 'number'\n  : t === 'string' ? typeof v === 'string'\n  : t === 'boolean' ? typeof v === 'boolean'\n  : t === 'array' ? Array.isArray(v)\n  : t === 'object' ? typeof v === 'object' && v !== null && !Array.isArray(v)\n  : false;","tryCatchPattern":"try {\n  await executor.execute(opId, args, ctx, { agentRun: run });\n} catch (e) {\n  if (e instanceof ValidationError && /Expected /.test(JSON.stringify(e.detail))) {\n    args = coerceToSchema(args, schema);\n    return await executor.execute(opId, args, ctx, { agentRun: run });\n  }\n  throw e;\n}","preventionTips":["Coerce numeric/boolean inputs before calling the API","Keep advertised tool schemas identical to Core API schemas","Remember booleans are rejected for integer/number fields by design"],"tags":["validation","types","schema","request-builder"],"backgroundTag":"schema-validation-failed","analyzedSha":"6ec464fabd61b95912d539455a3a5f15f5c59fe0","analyzedAt":"2026-08-28T11:33:00.925Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}