Significant-Gravitas/AutoGPT · error · NotFoundError

Workspace {ws_id} not found in org {expected_org_id}

Error message

Workspace {ws_id} not found in org {expected_org_id}

What it means

Raised by get_team() in team_db.py when the Team row exists but its orgId differs from expected_org_id supplied by the caller. It deliberately returns 'not found' (NotFoundError, HTTP 404) rather than 403 to avoid leaking the existence of workspaces in other organizations.

Source

Thrown at autogpt_platform/backend/backend/api/features/orgs/team_db.py:66

            "orgId": org_id,
            "archivedAt": None,
            "OR": [
                {"joinPolicy": "OPEN"},
                {"Members": {"some": {"userId": user_id, "status": "ACTIVE"}}},
            ],
        },
        order={"createdAt": "asc"},
    )
    return [TeamResponse.from_db(ws) for ws in workspaces]


async def get_team(ws_id: str, expected_org_id: str | None = None) -> TeamResponse:
    """Get workspace details. Validates org ownership if expected_org_id is given."""
    ws = await prisma.team.find_unique(where={"id": ws_id})
    if ws is None:
        raise NotFoundError(f"Workspace {ws_id} not found")
    if expected_org_id and ws.orgId != expected_org_id:
        raise NotFoundError(f"Workspace {ws_id} not found in org {expected_org_id}")
    return TeamResponse.from_db(ws)


async def update_team(ws_id: str, data: dict) -> TeamResponse:
    """Update workspace fields. Guards the default workspace join policy."""
    update_data = {k: v for k, v in data.items() if v is not None}
    if not update_data:
        return await get_team(ws_id)

    # Guard: default workspace joinPolicy cannot be changed
    if "joinPolicy" in update_data:
        ws = await prisma.team.find_unique(where={"id": ws_id})
        if ws and ws.isDefault:
            raise ValueError("Cannot change the default workspace's join policy")

    await prisma.team.update(where={"id": ws_id}, data=update_data)
    return await get_team(ws_id)

View on GitHub (pinned to 9c8bb5550f)

Solutions

  1. Fetch workspaces via the org-scoped list endpoint so ids always come from the same org as the URL.
  2. On 404, re-list the org's workspaces and drop stale ids from client state.
  3. Never construct team URLs by mixing a cached workspace id with a different org id.

Example fix

// before
router.push(`/organizations/${lastOrgId}/teams/${cachedTeamId}`);
// after
const teams = await api.get(`/api/orgs/${activeOrgId}/teams`).then(r => r.data);
const team = teams.find(t => t.id === cachedTeamId) ?? teams[0];
router.push(`/organizations/${activeOrgId}/teams/${team.id}`);
Defensive patterns

Strategy: validation

Validate before calling

const teams = await api.get(`/api/orgs/${activeOrgId}/teams`).then(r => r.data);
const inOrg = teams.some(t => t.id === wsId);
if (!inOrg) redirectToOrgTeamList();

Try / catch

try {
  return await get_team(ws_id, expected_org_id=org_id);
} except NotFoundError:
  raise HTTPException(404, 'Workspace not found in this organization')

Prevention

When it happens

Trigger: Accessing /api/orgs/{org_id}/teams/{ws_id} (or any route passing expected_org_id) where the workspace belongs to a different org — cross-tenant probing or a stale link after the workspace moved.

Common situations: User is member of multiple orgs and uses a workspace link under the wrong org path; workspace transferred between orgs; frontend builds URLs by concatenating an old org id with a remembered workspace id.

Related errors


AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14). Data as JSON: /api/errors/80ad3950341cdd77. Report an issue: GitHub.