different-ai/openwork · error

Failed to copy attachment

Error message

Failed to copy attachment "${metadata.filename}" into this worker workspace: expected ${file.size} bytes, wrote ${result.bytes}

What it means

After a successful upload the client verifies the server wrote exactly file.size bytes. A mismatch means a partial or corrupted write, so it throws this error with the expected vs actual byte counts. This protects tools from operating on truncated attachment copies.

Solutions

  1. Retry the upload to get a clean full write.
  2. Compare the source file on disk with the stored copy; re-attach if the file changed since attach time.
  3. Investigate the server storage layer for truncation of large files or transformations.

Example fix

// after uploadInbox resolves: if (result.bytes !== file.size) { console.error(`Upload truncated: expected ${file.size}, got ${result.bytes}`); } // then retry
Defensive patterns

Strategy: retry

Validate before calling

const size = attachment.file.size; if (size !== attachment.sizeAtAttach) { showToast(`"${attachment.filename}" changed since attach — re-attach it`); return; }

Try / catch

try { await composerAttachmentsToWorkspaceFileParts({ attachments, endpoint, sessionId, workspaceRoot }); } catch (e) { if (String(e.message).includes('bytes')) { await retryWithBackoff(() => sendWithAttachments()); } else { throw e; } }

Prevention

When it happens

Trigger: uploadInbox returns result.bytes !== file.size — interrupted transfer still reported success, server-side truncation, encoding/transformation of the file during storage, or a stale size from a re-encoded image.

Common situations: Flaky network with a lossy upload pipeline; server storage layer truncating large files; the file changed on disk between read and upload so sizes disagree.

Related errors


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

Appendix: source

Thrown at apps/app/src/react-app/domains/session/sync/attachment-file-part.ts:387

      filename: metadata.filename,
      id,
    });

    let result: InboxUploadResult;
    try {
      result = await input.endpoint.client.uploadInbox(workspaceId, file, { path: inboxPath });
    } catch (error) {
      throw new Error(uploadErrorMessage(metadata.filename, error));
    }

    if (result.ok === false) {
      throw new Error(`Failed to copy attachment "${metadata.filename}" into this worker workspace: upload was rejected`);
    }
    if (!result.path.trim()) {
      throw new Error(`Failed to copy attachment "${metadata.filename}" into this worker workspace: upload did not return a path`);
    }
    if (result.bytes !== file.size) {
      throw new Error(`Failed to copy attachment "${metadata.filename}" into this worker workspace: expected ${file.size} bytes, wrote ${result.bytes}`);
    }

    const workspacePath = workspaceInboxPath(result.path);
    const absolutePath = joinWorkspaceRelativePath(workspaceRoot, workspacePath);
    uploaded.push({
      filename: metadata.filename,
      mime: metadata.mime,
      bytes: result.bytes,
      workspacePath,
      url: toFileUrl(absolutePath),
      file,
    });
  }

  return [
    attachmentPathNotePart(uploaded),
    ...(await Promise.all(uploaded.map(uploadedAttachmentFilePart))),
  ];

View on GitHub (pinned to 2b7df46e8a)