Mintplex-Labs/anything-llm · error
Bad Request
Error message
Bad Request
What it means
POST /v1/workspace/:slug/thread/new returns 400 when Workspace.get({slug}) resolves to null — the workspace slug in the URL does not exist. The endpoint deliberately maps 'workspace not found' to 400 Bad Request (arguably it should be 404), so 'Bad Request' here means unknown slug, not malformed body. Request body shape ({userId, name, slug}) is not what triggers this.
Solutions
- GET /v1/workspaces and use the exact 'slug' field of the target workspace in the URL
- If the workspace is missing, create it first (POST /v1/workspace/new) and then retry the thread creation
- Do not pass the workspace name or numeric id in place of the slug — only the slug resolves
Example fix
// before
await api.post(`/v1/workspace/${name}/thread/new`, { name }); // 400: used display name
// after
const { workspaces } = await api.get("/v1/workspaces");
const ws = workspaces.find((w) => w.slug === slug);
if (!ws) throw new Error(`unknown workspace slug: ${slug}`);
await api.post(`/v1/workspace/${ws.slug}/thread/new`, { name }); Defensive patterns
Strategy: validation
Validate before calling
const { workspaces } = await fetch(`${base}/v1/workspaces`, {
headers: { Authorization: `Bearer ${apiKey}` },
}).then((r) => r.json());
if (!workspaces.some((w) => w.slug === slug))
throw new Error(`unknown workspace slug: '${slug}' — create it before opening a thread`); Type guard
/** This endpoint returns 400 for a missing workspace (not 404). */
function isUnknownWorkspace400(res) {
return res.status === 400 && !res.headers.get("content-length"); // bare sendStatus(400), empty body
} Try / catch
try {
const res = await postThread(slug, body);
if (res.status === 400) throw new Error(`workspace slug '${slug}' does not exist`);
} catch (e) {
console.error("thread create failed", e);
} Prevention
- Resolve slugs dynamically from GET /v1/workspaces instead of hard-coding
- Create the workspace (POST /v1/workspace/new) and assert success before threading against it
- Remember the response is an empty-bodied 400 — don't try to parse a JSON error message
When it happens
Trigger: POSTing to /v1/workspace/my-wrokspace/thread/new with a typo'd or deleted workspace slug; using the workspace name or ID instead of its slug; workspace slug changed via rename; newly created workspace not yet committed when the thread call races ahead.
Common situations: Hard-coded slugs in scripts drift after workspaces are renamed or recreated in CI/dev loops; copy-pasting a workspace name where the slug belongs; multi-tenant automation that assumes a workspace exists before it was actually created.
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
- Internal Server Error
- Message is empty
- is not a valid mode.
- Workspace or thread is not valid.
- AnthropicLLM::getChatCompletion failed to communicate with…
AI-assisted analysis of Mintplex-Labs/anything-llm@f92433b4ea (2026-08-18).
Data as JSON: /api/errors/aa5289771e72290a.
Report an issue: GitHub.
Appendix: source
Thrown at server/endpoints/api/workspaceThread/index.js:78
message: null
}
}
}
}
}
#swagger.responses[403] = {
schema: {
"$ref": "#/definitions/InvalidAPIKey"
}
}
*/
try {
const wslug = request.params.slug;
let { userId = null, name = null, slug = null } = reqBody(request);
const workspace = await Workspace.get({ slug: String(wslug) });
if (!workspace) {
response.sendStatus(400).end();
return;
}
// If the system is not multi-user and you pass in a userId
// it needs to be nullified as no users exist. This can still fail validation
// as we don't check if the userID is valid.
if (!response.locals.multiUserMode && !!userId) userId = null;
const { thread, message } = await WorkspaceThread.new(
workspace,
userId ? Number(userId) : null,
{ name, slug }
);
await Telemetry.sendTelemetry("workspace_thread_created", {
multiUserMode: multiUserMode(response),
LLMSelection: process.env.LLM_PROVIDER || "openai",
Embedder: process.env.EMBEDDING_ENGINE || "inherit",View on GitHub (pinned to f92433b4ea)