{"record":{"id":"9dfe5f15f25033e8","repo":"windmill-labs/windmill","slug":"the-session-workspace-could-not-be-created-or-comm","errorCode":null,"errorMessage":"the session workspace could not be created or committed (fork creation may have failed)","messagePattern":"the session workspace could not be created or committed \\(fork creation may have failed\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"frontend/src/lib/components/sessions/sessionRuntime.svelte.ts","lineNumber":416,"sourceCode":"\t// session targeting the right workspace. Both calls are idempotent.\n\tmanager.beforeSend = async () => {\n\t\tmaterializeTransient(session.id)\n\t\t// Session is now persisted → flush any linked files buffered while it was transient.\n\t\tawait manager.attachedFiles.flushPending()\n\t\t// Fork creation is the slow part of the pre-flight; label the loading\n\t\t// indicator so the user knows why the send is taking a moment.\n\t\tmanager.loadingLabel = 'Creating workspace fork...'\n\t\tconst committed = await commitSessionWorkspace(session.id, get(workspaceStore) ?? undefined)\n\t\tmanager.loadingLabel = undefined\n\t\t// commitSessionWorkspace returns undefined only when the session did NOT\n\t\t// commit to a workspace — most importantly when a staged fork failed to\n\t\t// materialise (materializeFork is built to toast + return undefined rather\n\t\t// than throw). Throwing here is what makes AIChatManager.sendRequest abort:\n\t\t// otherwise the send proceeds against get(workspaceStore) (the parent for a\n\t\t// staged-new-fork draft), shipping the message + its tool calls to the\n\t\t// wrong workspace while the pending_fork is silently dropped.\n\t\tif (!committed) {\n\t\t\tthrow new Error(\n\t\t\t\t'the session workspace could not be created or committed (fork creation may have failed)'\n\t\t\t)\n\t\t}\n\t\t// The composer may have been enabled by a previous workspace's copilot\n\t\t// config (copilotInfo is global). getCurrentModel() reads it when the\n\t\t// request builds just after this hook, so load the committed workspace's\n\t\t// config first — otherwise a send right after switching workspaces could\n\t\t// pick the old provider/model while the proxy + tools target the new one.\n\t\t// SessionWrapper's active-session load usually already did this; skip the\n\t\t// fetch when it matches (a staged fork commits to a fresh id its load missed).\n\t\tif (get(copilotWorkspace) !== committed) {\n\t\t\tawait loadCopilot(committed)\n\t\t}\n\t}\n\tmanager.afterFirstTurnSaved = () => generateAndApplySessionSummary(session.id, manager)\n\n\t// One cell per (kind, path). Created on demand by the load methods; each holds\n\t// cached content (KB–MB), not a mounted editor. Bounded to the items open","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/frontend/src/lib/components/sessions/sessionRuntime.svelte.ts#L398-L434","documentation":"createRuntime in sessionRuntime.svelte.ts throws this when a staged-new-fork session draft could not be materialised into a real workspace. materializeFork is designed to toast and return undefined instead of throwing, so the undefined return is converted into a throw here. Throwing is deliberate: it makes AIChatManager.sendRequest abort so the message and its tool calls are not shipped to the parent workspace while the pending fork is silently dropped.","triggerScenarios":"Calling getOrCreateRuntime/createRuntime for a session whose pending fork fails to materialize — typically because the backend rejected fork creation (workspace limit, backend error, license check) — so materializeFork returns undefined and `committed` is falsy.","commonSituations":"Sending an AI chat message from a session that is a staged fork draft; the backend rejects fork creation (e.g. CE workspace limit or transient backend failure), and the user's message would otherwise go to the wrong workspace.","solutions":["Inspect the earlier toast — materializeFork toasts the underlying backend rejection; fix that cause first (e.g. workspace-limit or backend error).","Retry sending after the fork creation issue is resolved, or switch the session to an existing (non-fork) workspace.","Check network/backend logs around the fork-creation API call for the root failure.","If this reproduces on every send, verify the session's pending_fork payload is valid (parent workspace, name) before calling sendRequest."],"exampleFix":"// before\nconst runtime = await getOrCreateRuntime(session) // throws\n// after\ntry {\n  const runtime = await getOrCreateRuntime(session)\n} catch {\n  // fork materialization failed: surface toast, abort send,\n  // let the user pick a non-fork workspace or retry\n  setSessionPendingWorkspace(session.id, null)\n}","handlingStrategy":"try-catch","validationCode":"const committed = await materializeFork(fork)\nif (!committed) throw new Error('fork creation failed — aborting send')","typeGuard":"function isCommitted(id: string | undefined | null): id is string {\n  return typeof id === 'string' && id.length > 0\n}","tryCatchPattern":"try {\n  const runtime = await getOrCreateRuntime(session)\n  await manager.sendRequest(runtime.workspaceId, message)\n} catch (e) {\n  if (String(e).includes('session workspace could not be created')) {\n    toast.error('Fork creation failed — message not sent. Pick a workspace or retry.')\n    return // do NOT fall back to the parent workspace\n  }\n  throw e\n}","preventionTips":["Never send chat requests against get(workspaceStore) for staged fork drafts — always go through the runtime","Handle materializeFork's undefined return at the call site instead of assuming success","Watch for the fork-failure toast and resolve its root cause (workspace limit, backend error) before re-sending"],"tags":["sessions","workspace","fork"],"backgroundTag":"workspace-fork-materialization-failed","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}