different-ai/openwork · error

Workspace endpoint is unavailable; attachments could not be

Error message

Workspace endpoint is unavailable; attachments could not be copied for tool access.

What it means

draftToParts converts a composer draft into message parts; when the draft has attachments it must have a workspace endpoint to upload them through. If endpoint is null/undefined while draft.attachments is non-empty, it throws this error before delegating to composerAttachmentsToWorkspaceFileParts.

Source

Thrown at apps/app/src/react-app/domains/session/sync/draft-parts.ts:51

  const toAbsolutePath = (path: string) => {
    const trimmed = path.trim();
    if (!trimmed) return "";
    if (trimmed.startsWith("/")) return trimmed;
    if (/^[a-zA-Z]:[\\/]/.test(trimmed)) return trimmed;
    if (!root) return "";
    return joinWorkspaceRelativePath(root, trimmed);
  };

  const filenameFromPath = (path: string) => {
    const normalized = path.replace(/\\/g, "/");
    const segments = normalized.split("/").filter(Boolean);
    return segments[segments.length - 1] ?? "file";
  };

  const attachmentFileById = new Map<string, FilePartInput>();
  if (draft.attachments.length > 0) {
    if (!endpoint) {
      throw new Error("Workspace endpoint is unavailable; attachments could not be copied for tool access.");
    }
    const uploaded = await composerAttachmentsToWorkspaceFileParts({
      attachments: draft.attachments,
      endpoint,
      sessionId,
      workspaceRoot: root,
    });
    for (const part of uploaded) {
      if (part.type === "text") {
        parts.push(part);
        continue;
      }
    }
    const fileParts = uploaded.filter((part): part is FilePartInput => part.type === "file");
    for (const [index, attachment] of draft.attachments.entries()) {
      const filePart = fileParts[index];
      if (filePart) attachmentFileById.set(attachment.id, filePart);
    }

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Resolve the workspace endpoint before converting/sending any draft that has attachments; strip or defer attachments if no endpoint exists.
  2. Ensure the draft store revalidates attachments against the current workspace on restore.
  3. Catch the error and prompt to reconnect the workspace before sending.

Example fix

// before: await draftToParts(draft, root, null, sessionId) | // after: const endpoint = resolveEndpoint(workspaceId); if (draft.attachments.length && !endpoint) { throw new Error('Reconnect the workspace before sending attachments'); } await draftToParts(draft, root, endpoint, sessionId)
Defensive patterns

Strategy: validation

Validate before calling

if (draft.attachments.length > 0 && !endpoint) { showToast('Reconnect the workspace before sending attachments'); return; } await draftToParts(draft, root, endpoint, sessionId);

Type guard

function canSendDraft(endpoint: unknown, draft: { attachments: unknown[] }): boolean { return draft.attachments.length === 0 || endpoint != null; }

Try / catch

try { await draftToParts(draft, root, endpoint, sessionId); } catch (e) { if (String(e.message).includes('Workspace endpoint is unavailable')) { await connectWorkspace(); } else { throw e; } }

Prevention

When it happens

Trigger: Calling draftToParts (e.g. via parts()) with a draft containing attachments but endpoint = null — typically sending a saved draft in a context without an active workspace endpoint (workspace closed, cloud session not provisioned, or store restored before connect).

Common situations: Restoring a persisted draft with attachments after app restart before the workspace connects; sending from a stale store after workspace switch; headless/automation callers passing no endpoint while drafts still carry attachments.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/e1818ca34b19b87d. Report an issue: GitHub.