HKUDS/DeepTutor · error · HTTPException

t("api.partner_not_found_or_not_running")

Error message

t("api.partner_not_found_or_not_running")

What it means

404 raised by the DELETE-stop route POST /api/partners/{partner_id}/stop when PartnerManager.stop_partner() returns a falsy value, meaning no partner with that id exists or it was not in a running state to stop. It is an explicit FastAPI HTTPException, so it always surfaces as an HTTP 404 with the i18n message 'api.partner_not_found_or_not_running'.

Source

Thrown at deeptutor/api/routers/partners.py:803

    mgr.save_config(partner_id, cfg)
    return _stopped_partner_dict(partner_id, cfg)


@router.post("/{partner_id}/start", dependencies=_MANAGEABLE)
async def start_partner(partner_id: str):
    instance = await _ensure_running_partner(partner_id, allow_stopped=True)
    # An explicit start is a persisted "run on boot" intent — so the partner
    # comes back in this state after a DeepTutor restart (a manual /stop clears
    # it; a lazy chat-driven start does NOT reach here, so it can't flip it).
    get_partner_manager().save_config(partner_id, instance.config, auto_start=True)
    return instance.to_dict(mask_secrets=True)


@router.post("/{partner_id}/stop", dependencies=_MANAGEABLE)
async def stop_partner(partner_id: str):
    stopped = await get_partner_manager().stop_partner(partner_id)
    if not stopped:
        raise HTTPException(status_code=404, detail=t("api.partner_not_found_or_not_running"))
    return {"partner_id": partner_id, "stopped": True}


@router.delete("/{partner_id}", dependencies=_MANAGEABLE)
async def destroy_partner(partner_id: str):
    destroyed = await get_partner_manager().destroy_partner(partner_id)
    if not destroyed:
        raise HTTPException(status_code=404, detail=t("api.partner_not_found"))
    return {"partner_id": partner_id, "destroyed": True}


@router.post("/{partner_id}/channels/reload", dependencies=_MANAGEABLE)
async def reload_partner_channels(partner_id: str):
    mgr = get_partner_manager()
    instance = mgr.get_partner(partner_id)
    if not instance or not instance.running:
        raise HTTPException(status_code=404, detail=t("api.partner_not_running"))
    try:

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Verify the partner exists and its running state via GET /partners before stopping (or use the list from GET /partners)
  2. Re-sync the partner list (GET /partners) and confirm the id, then retry the stop
  3. If the id is stale because the partner was destroyed, drop it from your UI state instead of retrying

Example fix

// before
await client.post(f"/partners/{partner_id}/stop")
// after
partners = (await client.get("/partners")).json()
if any(p["partner_id"] == partner_id for p in partners):
    await client.post(f"/partners/{partner_id}/stop")
Defensive patterns

Strategy: validation

Validate before calling

partners = (await client.get("/api/v1/partners")).json()
ids = {p["partner_id"] for p in partners}
assert partner_id in ids, "partner does not exist"

Try / catch

except HTTPStatusError as e:
    if e.response.status_code == 404:
        # already stopped or unknown id — refresh state

Prevention

When it happens

Trigger: Calling POST /partners/{id}/stop with an id that was never created, was already destroyed, or is already stopped (stop_partner returns False when nothing transitioned).

Common situations: UI holds a stale partner list after another tab destroyed the partner; double-clicking Stop fires the second request after the first already stopped it; typo'd partner_id from env/config.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/e844a4edd6fc8f63. Report an issue: GitHub.