{"record":{"id":"b69a86509909e6bb","repo":"HKUDS/DeepTutor","slug":"math-animator-config-must-be-an-object","errorCode":null,"errorMessage":"Math animator config must be an object.","messagePattern":"Math animator config must be an object\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/agents/math_animator/request_config.py","lineNumber":24,"sourceCode":"\nfrom pydantic import BaseModel, ConfigDict, Field, ValidationError\n\n\nclass MathAnimatorRequestConfig(BaseModel):\n    model_config = ConfigDict(extra=\"forbid\")\n\n    output_mode: Literal[\"video\", \"image\"] = \"video\"\n    quality: Literal[\"low\", \"medium\", \"high\"] = \"medium\"\n    style_hint: str = Field(default=\"\", max_length=500)\n\n\ndef validate_math_animator_request_config(\n    raw_config: dict[str, Any] | None,\n) -> MathAnimatorRequestConfig:\n    if raw_config is None:\n        return MathAnimatorRequestConfig()\n    if not isinstance(raw_config, dict):\n        raise ValueError(\"Math animator config must be an object.\")\n    try:\n        return MathAnimatorRequestConfig.model_validate(raw_config)\n    except ValidationError as exc:\n        details = \"; \".join(\n            f\"{'.'.join(str(part) for part in error['loc'])}: {error['msg']}\"\n            for error in exc.errors()\n        )\n        raise ValueError(f\"Invalid math animator config: {details}\") from exc\n\n\n__all__ = [\n    \"MathAnimatorRequestConfig\",\n    \"validate_math_animator_request_config\",\n]\n","sourceCodeStart":6,"sourceCodeEnd":39,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/agents/math_animator/request_config.py#L6-L39","documentation":"DocumentTooLargeError raised when the running sum of uncompressed member sizes exceeds _EPUB_MAX_TOTAL_UNCOMPRESSED_BYTES. This is the aggregate zip-bomb guard — many individually-small members whose total expansion is huge.","triggerScenarios":"An EPUB with thousands of small members totaling gigabytes uncompressed; crafted expansion bombs with high aggregate ratios.","commonSituations":"Malicious uploads; legitimately huge textbooks with uncompressed media bundled as many small files.","solutions":["Re-compress/re-package the EPUB with compressed media (Calibre re-export)","Reject with a clear message and enforce total-size limits at the upload layer","If legitimate content, split the book into volumes below the limit"],"exampleFix":"// before\ntext = extract_text_from_bytes(data, filename=\"bigbook.epub\")  # raises\n\n// after\nsubprocess.run([\"ebook-convert\",\"bigbook.epub\",\"bigbook2.epub\",\"--compress-images\"], check=True)\ntext = extract_text_from_bytes(Path(\"bigbook2.epub\").read_bytes(), filename=\"bigbook2.epub\")","handlingStrategy":"validation","validationCode":"import zipfile, io\nzf = zipfile.ZipFile(io.BytesIO(data))\ntotal = sum(i.file_size for i in zf.infolist())\nif total > 1_000_000_000:\n    reject(fn, \"expanded size too large\")","typeGuard":null,"tryCatchPattern":"except DocumentTooLargeError as e:\n    if \"uncompressed contents\" in str(e):\n        recompress_with_calibre(fn) and retry","preventionTips":["Enforce aggregate uncompressed-size caps at upload","Re-export EPUBs with compressed media"],"tags":["epub","zip-bomb","resource-limits"],"backgroundTag":"archive-expansion-too-large","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}