{"record":{"id":"31a4954ebd055837","repo":"HKUDS/DeepTutor","slug":"document-not-found","errorCode":null,"errorMessage":"Document not found","messagePattern":"Document not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"warning","filePath":"deeptutor/api/routers/co_writer.py","lineNumber":527,"sourceCode":"        raise HTTPException(status_code=404, detail=\"Tool call not found\")\n    except HTTPException:\n        raise\n    except Exception as e:\n        raise HTTPException(status_code=500, detail=str(e))\n\n\n# ─────────────────────────────────────────────────────────────────────────────\n# Document CRUD (multi-project Co-Writer)\n# ─────────────────────────────────────────────────────────────────────────────\n\n# Storage builds paths as `documents/doc_{doc_id}`; an unvalidated id like\n# \"a/../../x\" would escape the documents root (and DELETE runs rmtree).\n_DOC_ID_RE = re.compile(r\"^[0-9a-f]{8,32}$\")\n\n\ndef _validate_doc_id(doc_id: str) -> str:\n    if not _DOC_ID_RE.fullmatch(doc_id):\n        raise HTTPException(status_code=404, detail=\"Document not found\")\n    return doc_id\n\n\nclass CreateDocumentRequest(BaseModel):\n    title: str | None = None\n    content: str = \"\"\n\n\nclass UpdateDocumentRequest(BaseModel):\n    title: str | None = None\n    content: str | None = None\n\n\nclass DocumentResponse(BaseModel):\n    id: str\n    title: str\n    content: str\n    created_at: float","sourceCodeStart":509,"sourceCodeEnd":545,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/api/routers/co_writer.py#L509-L545","documentation":"404 raised by _validate_doc_id when the document id fails the strict regex ^[0-9a-f]{8,32}$ (8-32 lowercase hex chars). This is a security guard: the id is used to build a path and DELETE runs rmtree, so traversal like 'a/../../x' must never pass. Malformed ids are intentionally reported as 404, not 400, to avoid leaking the validation rule.","triggerScenarios":"Calling GET/PUT/DELETE /co-writer/documents/{doc_id} with an id containing uppercase, non-hex characters, slashes/dots, or one shorter than 8 / longer than 32 chars.","commonSituations":"Client generating its own ids instead of using the id returned by POST /documents, URL-encoding issues that mangle the id, or path-traversal probes.","solutions":["Always use the exact id returned by POST /co-writer/documents","Check the id matches ^[0-9a-f]{8,32}$ before calling","If you control id generation, switch to hex ids (e.g. uuid4().hex)"],"exampleFix":"// before\ndoc_id = str(uuid.uuid4())  # contains dashes -> 404\n// after\ndoc_id = uuid.uuid4().hex   # 32 lowercase hex chars","handlingStrategy":"validation","validationCode":"import re\nDOC_ID_RE = re.compile(r'^[0-9a-f]{8,32}$')\nassert DOC_ID_RE.fullmatch(doc_id), 'bad doc id'","typeGuard":"def is_valid_doc_id(doc_id: str) -> bool:\n    import re\n    return bool(re.fullmatch(r'[0-9a-f]{8,32}', doc_id))","tryCatchPattern":null,"preventionTips":["Only use ids returned by POST /co-writer/documents","Generate ids as uuid4().hex, never str(uuid4())","Never build ids from user input"],"tags":["co-writer","http-404","validation","path-traversal","doc-id"],"backgroundTag":"invalid-identifier-format","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}