json_error takes (status_code, message). Every refusal in the two
workspace routers passed them the other way round, which builds a
JSONResponse with a string status and raises TypeError inside
Response.init_headers before a byte is written. The branch meant to
explain a limit to the user returned 500 instead.
A member already holding the default two workspaces therefore got a 500
from the workspace page's Open workspace button rather than the quota
message. Twelve call sites corrected across both routers.
workspace_open also let ContainerError escape as a second 500 on the
same button when the ppy image is not built; it now returns 400 through
the shared fail helper.
tests/unit/responses.py AST-scans the whole package for both argument
orders, so the swap cannot reappear anywhere.