{"record":{"id":"eff04544a5f08ed6","repo":"ComposioHQ/composio","slug":"dynamic-key-schema-ref-must-be-a-string","errorCode":null,"errorMessage":"Dynamic-key schema `$ref` must be a string","messagePattern":"Dynamic-key schema `\\$ref` must be a string","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/composio/utils/schema_converter.py","lineNumber":615,"sourceCode":"    \"\"\"Reject external, anchored, and unresolved references before validation.\n\n    Only schema positions are walked. ``const``, ``default``, ``enum``, and\n    ``examples`` hold instance data, so a ``$ref``-shaped value stored there is\n    a payload rather than a reference and must not block tool wrapping.\n    \"\"\"\n    if checked_references is None:\n        checked_references = set()\n    if isinstance(schema, list):\n        for item in schema:\n            _check_dynamic_references(item, root_schema, checked_references)\n        return\n    if not isinstance(schema, dict):\n        return\n\n    reference = schema.get(\"$ref\")\n    if reference is not None:\n        if not isinstance(reference, str):\n            raise ValueError(\"Dynamic-key schema `$ref` must be a string\")\n        resolved = _resolve_local_json_pointer(reference, root_schema)\n        if not isinstance(resolved, (dict, bool)):\n            raise ValueError(\n                f\"Dynamic-key schema reference {reference!r} does not target a schema\"\n            )\n        if reference not in checked_references:\n            checked_references.add(reference)\n            _check_dynamic_references(resolved, root_schema, checked_references)\n\n    for keyword, value in schema.items():\n        if keyword in _SCHEMA_VALUED_KEYWORDS or keyword in _SCHEMA_LIST_KEYWORDS:\n            _check_dynamic_references(value, root_schema, checked_references)\n        elif keyword in _SCHEMA_MAP_KEYWORDS:\n            if isinstance(value, dict):\n                for entry in value.values():\n                    _check_dynamic_references(entry, root_schema, checked_references)\n        elif keyword == \"items\":\n            # A schema, or a list of schemas for tuple validation.","sourceCodeStart":597,"sourceCodeEnd":633,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/utils/schema_converter.py#L597-L633","documentation":"While recursively checking dynamic-key schemas, _check_dynamic_references found a \"$ref\" whose value is not a string (e.g. a dict, list, or number). Per JSON Schema, $ref must be a string URI/reference, so the converter rejects the schema at construction time with ValueError.","triggerScenarios":"A schema node contains \"$ref\": {\"type\": \"string\"} (a nested schema accidentally used as the ref value) or \"$ref\": 123 — typically an authoring or transformation bug where the intended schema was placed under the wrong key.","commonSituations":"Machine-generated schemas from template bugs; YAML-to-JSON conversions that misplace nodes; prompt-generated schemas from LLMs; refactoring that wrapped the target schema instead of referencing it.","solutions":["Find the non-string $ref in the tool schema and replace its value with the correct string pointer (e.g. \"#/$defs/thing\") — usually the object next to it is the intended target and should live under definitions","Validate the schema with a JSON Schema meta-validator before feeding it to the SDK","Fix the generator/template that produced the malformed ref"],"exampleFix":"# before\n{\"$ref\": {\"type\": \"string\"}}\n# after\n{\"$ref\": \"#/$defs/target\"}","handlingStrategy":"validation","validationCode":"def refs_are_strings(schema):\n    if isinstance(schema, dict):\n        if \"$ref\" in schema and not isinstance(schema[\"$ref\"], str):\n            return False\n        return all(refs_are_strings(v) for v in schema.values())\n    if isinstance(schema, list):\n        return all(refs_are_strings(i) for i in schema)\n    return True","typeGuard":null,"tryCatchPattern":"try:\n    build_model(schema)\nexcept ValueError as e:\n    if \"$ref` must be a string\" in str(e):\n        schema = fix_malformed_refs(schema)","preventionTips":["Lint generated schemas for non-string $ref values","Validate against the JSON Schema metaschema before conversion","Avoid hand-building refs via string concatenation of unknown types"],"tags":["json-schema","ref-resolution","malformed-schema"],"backgroundTag":"malformed-json-schema","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}