{"record":{"id":"c7893a7bfb48b1a1","repo":"HKUDS/DeepTutor","slug":"generated-code-does-not-define-a-renderable-manim","errorCode":null,"errorMessage":"Generated code does not define a renderable Manim Scene class.","messagePattern":"Generated code does not define a renderable Manim Scene class\\.","errorType":"exception","errorClass":"ManimRenderError","httpStatus":null,"severity":"error","filePath":"deeptutor/agents/math_animator/renderer.py","lineNumber":226,"sourceCode":"    def _find_rendered_file(self, suffix: str) -> Path:\n        # Manim stores many transient chunks under ``partial_movie_files``.\n        # We only want the final exported artifact for the scene.\n        matches = [\n            path\n            for path in self.media_dir.rglob(f\"*{suffix}\")\n            if \"partial_movie_files\" not in path.parts\n        ]\n        if not matches:\n            matches = list(self.media_dir.rglob(f\"*{suffix}\"))\n        if not matches:\n            raise ManimRenderError(f\"Rendered {suffix} artifact not found.\")\n        return max(matches, key=lambda path: path.stat().st_mtime)\n\n    @staticmethod\n    def _extract_scene_name(code: str) -> str:\n        match = SCENE_PATTERN.search(code)\n        if not match:\n            raise ManimRenderError(\"Generated code does not define a renderable Manim Scene class.\")\n        return match.group(1)\n\n    def _build_artifact(\n        self,\n        artifact_path: Path,\n        artifact_type: str,\n        content_type: str,\n        label: str,\n    ) -> RenderedArtifact:\n        rel_path = artifact_path.resolve().relative_to(self.path_service.user_data_dir.resolve())\n        return RenderedArtifact(\n            type=artifact_type,\n            filename=artifact_path.name,\n            url=f\"/api/outputs/{rel_path.as_posix()}\",\n            content_type=content_type,\n            label=label,\n        )\n","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/agents/math_animator/renderer.py#L208-L244","documentation":"DocumentTooLargeError raised when a single EPUB member's uncompressed file_size exceeds _EPUB_MAX_MEMBER_BYTES. Guards against decompressing one gigantic file (classic zip-bomb member) before reading it.","triggerScenarios":"An EPUB containing a single multi-hundred-MB HTML/image asset; crafted archive whose header declares an oversized member.","commonSituations":"EPUBs embedding raw high-res images or uncompressed media; malicious archives targeting extraction pipelines.","solutions":["Split or compress oversized assets, or remove the huge member if it's an image not needed for text extraction","Re-export the EPUB with image downscaling (ebook-convert --enable-heuristics / image compression)","Enforce per-file upload limits before ingestion"],"exampleFix":"// before\ntext = extract_text_from_bytes(data, filename=\"bomb.epub\")  # raises\n\n// after\nimport zipfile\nzf = zipfile.ZipFile(io.BytesIO(data))\nkeep = [i.filename for i in zf.infolist() if i.file_size < 50_000_000]\n# rebuild epub without the oversized member, then extract","handlingStrategy":"validation","validationCode":"import zipfile, io\nzf = zipfile.ZipFile(io.BytesIO(data))\nif any(i.file_size > 100_000_000 for i in zf.infolist()):\n    reject(fn, \"contains oversized member\")","typeGuard":null,"tryCatchPattern":"except DocumentTooLargeError as e:\n    if \"member\" in str(e) and \"too large\" in str(e):\n        strip_oversized_members_and_retry(data)","preventionTips":["Inspect infolist() sizes before extraction","Compress or drop huge embedded assets pre-upload"],"tags":["epub","zip-bomb","resource-limits"],"backgroundTag":"archive-member-too-large","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}