odysseus-dev/odysseus · error · HTTPException
Codex plugin bundle not found
Error message
Codex plugin bundle not found
What it means
Raised as HTTP 404 by GET /api/codex/plugin.zip when the server cannot find the integrations/codex directory relative to the routes package. The zip is built live from that directory at request time, so the error means the plugin source files simply are not present in the deployment — a packaging/install problem, not a runtime failure.
Source
Thrown at routes/codex_routes.py:223
},
"cookbook": {
"read": scoped(COOKBOOK_READ_SCOPES),
"launch": scoped(COOKBOOK_LAUNCH_SCOPES),
"actions": ["tasks", "servers", "output", "serve", "stop"],
},
},
"safety": {
"email_send_requires_confirmation": True,
"destructive_actions_should_confirm": True,
},
}
@router.get("/plugin.zip")
def plugin_zip(request: Request):
require_authenticated_request(request)
root = Path(__file__).resolve().parent.parent / "integrations" / "codex"
if not root.exists():
raise HTTPException(404, "Codex plugin bundle not found")
buf = BytesIO()
with zipfile.ZipFile(buf, "w", compression=zipfile.ZIP_DEFLATED) as zf:
for path in sorted(root.rglob("*")):
if path.is_dir() or "__pycache__" in path.parts or path.suffix == ".pyc":
continue
zf.write(path, Path("odysseus") / path.relative_to(root))
buf.seek(0)
headers = {"Content-Disposition": 'attachment; filename="odysseus-codex-plugin.zip"'}
return StreamingResponse(buf, media_type="application/zip", headers=headers)
@router.get("/todos")
async def list_todos(request: Request, archived: bool = False, label: str | None = None):
owner = _scope_owner(request, TODO_READ_SCOPES)
args: dict[str, Any] = {"action": "list", "archived": archived}
if label:
args["label"] = label
return await do_manage_notes(json.dumps(args), owner=owner)
View on GitHub (pinned to f9235ebbf1)
Solutions
- Verify integrations/codex exists next to the routes package in the deployed tree; restore it if the build step excluded it.
- For PyInstaller, add integrations/codex as datas in the spec file and rebuild.
- For Docker, ensure the COPY step includes integrations/ and it is not in .dockerignore.
- Update/reinstall the application so the shipped layout matches what the route expects.
Example fix
# before (Dockerfile) COPY app.py routes/ ./ # after COPY app.py routes/ integrations/ ./
Defensive patterns
Strategy: try-catch
Validate before calling
async function downloadPluginZip() {
const head = await fetch('/api/codex/plugin.zip', {method: 'HEAD'});
if (head.status === 404) {
console.error('Plugin bundle missing from this deployment — packaging issue.');
return null;
}
return fetch('/api/codex/plugin.zip');
} Try / catch
try { zip = await getPluginZip() } catch (e) { if (e.status === 404) { showInstallHint('Server build lacks integrations/codex; reinstall server') ; return } throw } Prevention
- Include integrations/ in build specs (PyInstaller datas, Docker COPY, package data).
- Add a smoke test hitting /api/codex/plugin.zip in CI to catch packaging regressions.
- Fail CI if the directory is absent rather than discovering it at download time.
When it happens
Trigger: Running from a PyInstaller bundle, Docker image, or source checkout where integrations/codex was excluded from the artifact; the directory renamed or moved; running app.py from a different location so Path(__file__).parent.parent resolves elsewhere.
Common situations: One-file PyInstaller builds (Odysseus.spec) omitting data directories; .dockerignore excluding integrations/; shallow clone missing submodules; stale installed package lacking new files after an upgrade.
Related errors
- Stream closed before completion
- Server error ' + res.status
- Integration not found
- Calendar not found
- Event not found
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/98c5f3e206bb1a83.
Report an issue: GitHub.