{"record":{"id":"b266ae0c898a405c","repo":"bytedance/deer-flow","slug":"path-is-not-a-file-path","errorCode":null,"errorMessage":"Path is not a file: {path}","messagePattern":"Path is not a file: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"backend/app/gateway/routers/artifacts.py","lineNumber":85,"sourceCode":"\ndef _normalize_editable_artifact_path(path: str) -> str:\n    stripped = path.lstrip(\"/\")\n    if not stripped.startswith(_EDITABLE_OUTPUTS_PREFIX):\n        raise HTTPException(status_code=400, detail=\"Only files in /mnt/user-data/outputs can be edited\")\n    if \".skill/\" in stripped or stripped.endswith(\".skill\"):\n        raise HTTPException(status_code=415, detail=\"Skill archives cannot be edited in the artifacts panel\")\n    return f\"/{stripped}\"\n\n\ndef _load_editable_artifact(actual_path: Path, path: str, expected_sha256: str) -> tuple[bytes, os.stat_result]:\n    try:\n        file_stat = os.lstat(actual_path)\n    except FileNotFoundError:\n        raise HTTPException(status_code=404, detail=f\"Artifact not found: {path}\") from None\n    if stat.S_ISLNK(file_stat.st_mode):\n        raise HTTPException(status_code=415, detail=\"Symlinked artifacts cannot be edited\")\n    if not stat.S_ISREG(file_stat.st_mode):\n        raise HTTPException(status_code=400, detail=f\"Path is not a file: {path}\")\n    if file_stat.st_size > MAX_EDITABLE_ARTIFACT_BYTES:\n        raise HTTPException(status_code=413, detail=\"Artifact is too large to edit\")\n\n    current = actual_path.read_bytes()\n    if len(current) > MAX_EDITABLE_ARTIFACT_BYTES:\n        raise HTTPException(status_code=413, detail=\"Artifact is too large to edit\")\n    if b\"\\x00\" in current:\n        raise HTTPException(status_code=415, detail=\"Binary artifacts cannot be edited\")\n    try:\n        current.decode(\"utf-8\")\n    except UnicodeDecodeError:\n        raise HTTPException(status_code=415, detail=\"Only UTF-8 text artifacts can be edited\") from None\n\n    current_sha256 = hashlib.sha256(current).hexdigest()\n    if current_sha256 != expected_sha256:\n        raise HTTPException(status_code=412, detail=\"Artifact changed since it was opened\")\n    return current, file_stat\n","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/artifacts.py#L67-L103","documentation":"HTTP 400 from _load_editable_artifact: lstat succeeded but the mode is not S_ISREG (and not a symlink, which 415 catches first). The path exists but is a directory, FIFO, device, or socket — the text editor only operates on regular files.","triggerScenarios":"PUT edit-artifact with a path that resolves to a directory (e.g. 'mnt/user-data/outputs/report/' with trailing content being a dir) or a special file created by sandbox tooling.","commonSituations":"Path-building bugs appending a filename to a directory path that already ends at the directory, artifacts listings surfacing directories as entries, or agent-created FIFOs/device nodes in outputs.","solutions":["Fix the client path to point at a concrete file inside the directory","Filter directories out of anything the user can click Edit on","If a special file is intentional, it is not editable — remove it from the editor surface"],"exampleFix":"// before\nawait client.put(EDIT_URL, json={\"path\": \"mnt/user-data/outputs/report\", ...})\n\n// after\nawait client.put(EDIT_URL, json={\"path\": \"mnt/user-data/outputs/report/summary.md\", ...})","handlingStrategy":"validation","validationCode":"import os, stat\n\ndef is_regular_file(path: str) -> bool:\n    try:\n        return stat.S_ISREG(os.lstat(path).st_mode)\n    except OSError:\n        return False","typeGuard":"import os, stat\n\ndef is_editable_target(path: str) -> bool:\n    \"\"\"Regular non-symlink file under outputs.\"\"\"\n    try:\n        st = os.lstat(path)\n    except OSError:\n        return False\n    return stat.S_ISREG(st.st_mode) and path.lstrip(\"/\").startswith(\"mnt/user-data/outputs/\")","tryCatchPattern":null,"preventionTips":["Never offer Edit actions on directory entries in artifact trees","Validate that constructed paths end in a filename, not a directory","Remember order: symlink → 415, non-regular → 400, so client checks should mirror that"],"tags":["api","artifacts","path-validation","http-400","filesystem"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}