{"record":{"id":"1287a457ebaf1c3e","repo":"toeverything/AFFiNE","slug":"local-workspaces-don-t-support-attachments-or-refe","errorCode":null,"errorMessage":"Local workspaces don't support attachments or references.","messagePattern":"Local workspaces don't support attachments or references\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/plugins/copilot/conversation/inbox.ts","lineNumber":68,"sourceCode":"      options.blob ? [options.blob] : options.blobs || []\n    );\n\n    const focusSelectors = options.params?.focusSelectors;\n    const hasWorkspaceContext =\n      attachments.length > 0 ||\n      blobs.length > 0 ||\n      (Array.isArray(options.params?.scopeSelectors) &&\n        options.params.scopeSelectors.length > 0) ||\n      (Array.isArray(options.params?.preferredSourceIds) &&\n        options.params.preferredSourceIds.length > 0) ||\n      (focusSelectors === undefined\n        ? session.config.focus.selectors.length > 0\n        : Array.isArray(focusSelectors) && focusSelectors.length > 0);\n    if (\n      hasWorkspaceContext &&\n      !(await this.models.workspace.get(session.config.workspaceId))\n    ) {\n      throw new BadRequestException(\n        \"Local workspaces don't support attachments or references.\"\n      );\n    }\n\n    if (blobs.length) {\n      await this.ac\n        .user(userId)\n        .workspace(session.config.workspaceId)\n        .allowLocal()\n        .assert('Workspace.Blobs.Write');\n    }\n\n    for (const blob of blobs) {\n      const uploaded = await this.storage.handleUpload(userId, blob);\n      const detectedMime =\n        sniffMime(uploaded.buffer, blob.mimetype)?.toLowerCase() ||\n        blob.mimetype;\n      let attachmentBuffer = uploaded.buffer;","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/copilot/conversation/inbox.ts#L50-L86","documentation":"Thrown when a message carries workspace-scoped context (attachments, blobs, scopeSelectors, preferredSourceIds, or focus selectors) but session.config.workspaceId does not resolve to a workspace via models.workspace.get — i.e. the session is bound to a local (client-side) workspace the server has no record of. Local workspaces have no server-side blob/doc storage, so references cannot be resolved. It surfaces as a BadRequestException (HTTP 400).","triggerScenarios":"Sending createMessage with blobs, attachments, scopeSelectors, preferredSourceIds, or non-empty focus selectors while the session's workspaceId is a local workspace id; local docs synced without a server workspace record; a workspace deleted server-side while the session still references it.","commonSituations":"Client enables file attachments in a local-only AFFiNE workspace; doc reference chips added in local mode; workspace was deleted but an old copilot session pinned its id.","solutions":["Strip attachments/blobs/scopeSelectors/preferredSourceIds/focusSelectors from the request when operating on a local workspace","Enable cloud sync / sign in so the workspace exists server-side before attaching context","Verify session.config.workspaceId is a cloud workspace id (models.workspace.get returns a record) before building the message","In the UI, disable attachment and reference controls for local-workspace sessions"],"exampleFix":"// before\nawait inbox.createMessage(userId, {\n  sessionId,\n  blobs: [fileBlob], // local workspace -> 400\n});\n\n// after\nconst ws = await models.workspace.get(session.config.workspaceId);\nif (!ws) {\n  await inbox.createMessage(userId, { sessionId, content }); // plain prompt, no context\n} else {\n  await inbox.createMessage(userId, { sessionId, content, blobs: [fileBlob] });\n}","handlingStrategy":"validation","validationCode":"const isCloudWorkspace = !!(await models.workspace.get(session.config.workspaceId));\nconst hasContext =\n  (options.attachments?.length ?? 0) > 0 ||\n  (options.blobs?.length ?? 0) > 0 ||\n  (options.params?.scopeSelectors?.length ?? 0) > 0 ||\n  (options.params?.preferredSourceIds?.length ?? 0) > 0;\nif (!isCloudWorkspace && hasContext) {\n  delete options.attachments; delete options.blobs; delete options.params?.scopeSelectors; delete options.params?.preferredSourceIds;\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await inbox.createMessage(userId, options);\n} catch (e) {\n  if (e instanceof BadRequestException && /Local workspaces/.test(e.message)) {\n    return inbox.createMessage(userId, { ...options, attachments: undefined, blobs: undefined });\n  }\n  throw e;\n}","preventionTips":["Check workspace.isLocal before enabling attachment/reference UI affordances","Default focus selectors to [] when none are explicitly chosen","Test copilot flows in both local and cloud workspace modes"],"tags":["copilot","local-workspace","attachments","bad-request"],"backgroundTag":"local-workspace-limitation","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}