{"record":{"id":"a436be3eae6b592d","repo":"HKUDS/DeepTutor","slug":"capability-config-must-be-an-object","errorCode":null,"errorMessage":"Capability config must be an object.","messagePattern":"Capability config must be an object\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/runtime/request_contracts.py","lineNumber":78,"sourceCode":"        \"chartjs\",\n        \"mermaid\",\n        \"html\",\n        \"manim_video\",\n        \"manim_image\",\n    ] = \"auto\"\n    # Only meaningful when the routed render_type is manim_video / manim_image\n    # (either chosen explicitly or selected by AnalysisAgent in auto mode).\n    # Mirrors MathAnimatorRequestConfig defaults so the auto path stays\n    # zero-config.\n    quality: Literal[\"low\", \"medium\", \"high\"] = \"medium\"\n    style_hint: str = Field(default=\"\", max_length=500)\n\n\ndef _clean_public_config(raw_config: dict[str, Any] | None) -> dict[str, Any]:\n    if raw_config is None:\n        return {}\n    if not isinstance(raw_config, dict):\n        raise ValueError(\"Capability config must be an object.\")\n    cleaned = dict(raw_config)\n    for key in _RUNTIME_ONLY_KEYS:\n        cleaned.pop(key, None)\n    return cleaned\n\n\ndef _validate_model(\n    model_type: type[BaseModel],\n    raw_config: dict[str, Any] | None,\n    *,\n    label: str,\n) -> BaseModel:\n    cleaned = _clean_public_config(raw_config)\n    try:\n        return model_type.model_validate(cleaned)\n    except ValidationError as exc:\n        details = \"; \".join(\n            f\"{'.'.join(str(part) for part in error['loc'])}: {error['msg']}\"","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/runtime/request_contracts.py#L60-L96","documentation":"_clean_public_config rejects a request-level capability config that is not a JSON object/dict. Configs arriving as lists, strings, numbers, or booleans (anything non-None non-dict) raise this ValueError before Pydantic validation runs.","triggerScenarios":"Sending a chat/deep_solve/deep_question/visualize request (via WebSocket API or SDK) with `config` set to e.g. a JSON array, a bare string, or a number instead of an object.","commonSituations":"Frontend sending JSON where config is double-encoded (a JSON string containing JSON); clients constructing the payload with the wrong shape; SDK users passing a list of options instead of a dict; copy-paste errors from YAML configs pasted as strings.","solutions":["Make the request's config field a JSON object, e.g. {\"config\": {\"render_mode\": \"svg\"}}","If config is double-encoded JSON, parse it once before sending","Omit config entirely (null is accepted and treated as {}) when you have no config"],"exampleFix":"# before\n{\"config\": \"{\\\"render_mode\\\": \\\"svg\\\"}\"}\n# after\n{\"config\": {\"render_mode\": \"svg\"}}","handlingStrategy":"type-guard","validationCode":"if config is not None and not isinstance(config, dict):\n    raise TypeError(\"config must be an object or null\")\n# or if it might be a JSON string:\nimport json\nif isinstance(config, str):\n    config = json.loads(config)","typeGuard":"def is_valid_config_shape(cfg: object) -> bool:\n    return cfg is None or isinstance(cfg, dict)","tryCatchPattern":"try:\n    validate_chat_request_config(cfg)\nexcept ValueError as e:\n    if \"must be an object\" in str(e):\n        cfg = {}  # or re-shape client payload\n        result = validate_chat_request_config(cfg)","preventionTips":["Always build request payloads with dict literals for config","Never double-encode JSON strings","Add client-side schema checks (e.g. zod/pydantic) mirroring the server contract"],"tags":["request-validation","json","api","config"],"backgroundTag":"request-body-schema-mismatch","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}