{"record":{"id":"28827544fc28b448","repo":"rohitg00/ai-engineering-from-scratch","slug":"schema-for-location-must-be-an-object","errorCode":null,"errorMessage":"schema for {location} must be an object","messagePattern":"schema for (.+?) must be an object","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"certifications/claude/lessons/10-tool-use-and-agentic-loops/code/main.py","lineNumber":110,"sourceCode":"        \"object\": lambda item: isinstance(item, dict),\n    }\n    if expected not in checks:\n        raise ValueError(f\"unsupported schema type: {expected}\")\n    return checks[expected](value)\n\n\ndef _integer_bound(schema: dict[str, Any], name: str) -> int | None:\n    if name not in schema:\n        return None\n    value = schema[name]\n    if not isinstance(value, int) or isinstance(value, bool) or value < 0:\n        raise ValueError(f\"schema {name} must be a non-negative integer\")\n    return value\n\n\ndef _validate_schema_value(value: Any, schema: Any, location: str) -> None:\n    if not isinstance(schema, dict):\n        raise ValueError(f\"schema for {location} must be an object\")\n\n    declared_type = schema.get(\"type\")\n    if declared_type is not None:\n        declared_types = declared_type if isinstance(declared_type, list) else [declared_type]\n        if not declared_types or not all(isinstance(item, str) for item in declared_types):\n            raise ValueError(f\"schema type for {location} must be a string or non-empty string list\")\n        if not any(_matches_json_type(value, item) for item in declared_types):\n            expected = \" or \".join(declared_types)\n            raise ValueError(f\"invalid type for {location}: expected {expected}\")\n\n    if \"enum\" in schema:\n        choices = schema[\"enum\"]\n        if not isinstance(choices, list) or not choices:\n            raise ValueError(f\"schema enum for {location} must be a non-empty list\")\n        if value not in choices:\n            raise ValueError(f\"invalid value for {location}: not in enum\")\n\n    if isinstance(value, (int, float)) and not isinstance(value, bool):","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/certifications/claude/lessons/10-tool-use-and-agentic-loops/code/main.py#L92-L128","documentation":"_validate_schema_value raises ValueError(f'schema for {location} must be an object') when a subschema at the given location is not a dict - e.g. a property schema that is a string, list, or None. JSON Schema subschemas must be objects, and this recursive validator asserts that shape before reading 'type', 'enum', or bounds. The location string pinpoints exactly which property or items subschema is malformed.","triggerScenarios":"A tool input_schema like {\"properties\": {\"q\": \"string\"}} (bare type string instead of {\"type\": ...}); {\"items\": [\"string\"]} (list of schemas where one schema object is expected); a property set to None. Hit from validate() at the root if the top schema is not a dict, or recursively for any child.","commonSituations":"Writing shorthand schemas from memory ('name': 'string'); mixing JSON Schema and OpenAPI styles; programmatic schema assembly where a branch returns None.","solutions":["Wrap every subschema in an object: {\"q\": {\"type\": \"string\"}} not {\"q\": \"string\"}.","Read the {location} in the message to find the exact offending property or items node.","When assembling schemas in code, assert isinstance(subschema, dict) at build time.","Validate the schema itself once at startup before using it in tool definitions or model calls."],"exampleFix":"# before\n{\"type\": \"object\", \"properties\": {\"q\": \"string\"}}\n# ValueError: schema for $.q must be an object\n\n# after\n{\"type\": \"object\", \"properties\": {\"q\": {\"type\": \"string\"}}}","handlingStrategy":"type-guard","validationCode":"def subschemas_are_objects(schema, location=\"$\") -> bool:\n    if not isinstance(schema, dict):\n        return False\n    for name, sub in schema.get(\"properties\", {}).items():\n        if not subschemas_are_objects(sub, f\"{location}.{name}\"):\n            return False\n    if \"items\" in schema and not subschemas_are_objects(schema[\"items\"], location + \"[0]\"):\n        return False\n    return True","typeGuard":"from typing import Any\ndef is_object_schema(schema: Any) -> bool:\n    \"\"\"A schema node must itself be a dict.\"\"\"\n    return isinstance(schema, dict)","tryCatchPattern":"try:\n    validate(value, schema)\nexcept ValueError as exc:\n    if \"schema for\" in str(exc) and \"must be an object\" in str(exc):\n        raise SchemaError(f\"malformed schema: {exc}\") from exc  # author bug, not data bug\n    raise","preventionTips":["Always write subschemas as {\"type\": ...} objects, never bare strings.","Assert each generated schema node is a dict before insertion.","Remember 'items' holds ONE schema object, not a list of them."],"tags":["python","json-schema","validation","tool-definitions"],"backgroundTag":"malformed-json-schema","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}