{"record":{"id":"c854d5b76013a806","repo":"different-ai/openwork","slug":"workspace-endpoint-is-unavailable-attachments-cou","errorCode":null,"errorMessage":"Workspace endpoint is unavailable; attachments could not be copied for tool access.","messagePattern":"Workspace endpoint is unavailable; attachments could not be copied for tool access\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/app/src/react-app/domains/session/sync/attachment-file-part.ts","lineNumber":352,"sourceCode":"}\n\nexport async function composerAttachmentsToWorkspaceFileParts(input: {\n  attachments: ComposerAttachment[];\n  endpoint: ChatAttachmentWorkspaceEndpoint;\n  sessionId: string;\n  workspaceRoot: string;\n  createId?: () => string;\n}): Promise<Array<TextPartInput | FilePartInput>> {\n  if (input.attachments.length === 0) return [];\n\n  const workspaceRoot = input.workspaceRoot.trim();\n  if (!workspaceRoot) {\n    throw new Error(\"Workspace path is unavailable; attachments could not be copied for tool access.\");\n  }\n\n  const workspaceId = input.endpoint.workspaceId.trim();\n  if (!workspaceId) {\n    throw new Error(\"Workspace endpoint is unavailable; attachments could not be copied for tool access.\");\n  }\n\n  const uploaded: UploadedChatAttachment[] = [];\n  for (const attachment of input.attachments) {\n    // Oversized images are re-encoded here, at send time, so the composer chip\n    // appears instantly at attach time and the canvas work happens while the\n    // chip already shows its uploading state. When the transport uploads the\n    // original file from its local path, re-encoding would detach that path,\n    // so the original bytes are sent instead.\n    const file = input.endpoint.client.uploadInboxPrefersOriginalFile?.(attachment.file)\n      ? attachment.file\n      : await compressImageFile(attachment.file);\n    const metadata = resolveAttachmentFileMetadata(file);\n    const id = input.createId ? input.createId() : randomAttachmentId();\n    const inboxPath = buildChatAttachmentInboxPath({\n      sessionId: input.sessionId,\n      filename: metadata.filename,\n      id,","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/app/src/react-app/domains/session/sync/attachment-file-part.ts#L334-L370","documentation":"composerAttachmentsToWorkspaceFileParts requires a workspace endpoint carrying both a client and a workspaceId to upload attachment files into the worker inbox. When input.endpoint.workspaceId is empty/whitespace it throws this error; attachments cannot be copied for tool access without a valid endpoint.","triggerScenarios":"Calling composerAttachmentsToWorkspaceFileParts with an endpoint whose workspaceId is '' or whitespace — e.g. endpoint derived before workspace provisioning completed, or a draft restored for a workspace that no longer exists.","commonSituations":"Sending an attachment-laden draft after the workspace was deleted or re-created with a new id; endpoint built from an incomplete workspace record; offline/cloud mode where workspaceId is not yet assigned.","solutions":["Ensure the workspace endpoint (including workspaceId) is fully provisioned before sending messages with attachments.","Re-resolve the endpoint for the current workspace and retry.","Catch the error and inform the user the workspace endpoint is unavailable and to re-open the workspace."],"exampleFix":"// before: await composerAttachmentsToWorkspaceFileParts({ attachments, endpoint, ... }) | // after: if (!endpoint?.workspaceId?.trim()) throw new Error('Workspace not ready'); await composerAttachmentsToWorkspaceFileParts({ attachments, endpoint, ... })","handlingStrategy":"validation","validationCode":"if (!endpoint?.workspaceId?.trim()) { showToast('Workspace endpoint is not ready; cannot attach files'); return; } await composerAttachmentsToWorkspaceFileParts({ attachments, endpoint, workspaceRoot, sessionId });","typeGuard":"function hasWorkspaceEndpoint(endpoint: { workspaceId: string } | null | undefined): endpoint is { workspaceId: string } { return !!endpoint && endpoint.workspaceId.trim().length > 0; }","tryCatchPattern":"try { await composerAttachmentsToWorkspaceFileParts({ attachments, endpoint, workspaceRoot, sessionId }); } catch (e) { if (String(e.message).includes('Workspace endpoint is unavailable')) { await reprovisionEndpoint(); } else { throw e; } }","preventionTips":["Provision the full endpoint (client + workspaceId) before enabling sends with attachments.","Re-resolve the endpoint whenever the active workspace changes.","Detect deleted/recreated workspaces and invalidate cached endpoints."],"tags":["attachments","workspace","endpoint","precondition"],"backgroundTag":"missing-workspace-endpoint","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}