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
- Fetch workspaces via the org-scoped list endpoint so ids always come from the same org as the URL.
- On 404, re-list the org's workspaces and drop stale ids from client state.
- 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
- Always obtain workspace ids from the org-scoped list endpoint
- Never combine a cached workspace id with a different org path
- Treat 404 as non-existence (by design — it hides cross-org workspaces)
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
- Workspace {ws_id} not found
- Execution not found
- job not found
- Organization {org_id} not found
- Invitation {invitation_id} not found
AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14).
Data as JSON: /api/errors/80ad3950341cdd77.
Report an issue: GitHub.