different-ai/openwork · error

A valid transferId is required.

Error message

A valid transferId is required.

What it means

desktopTransferKey() validates the transferId supplied by a renderer over IPC before keying an active-transfer map. A valid id must be a non-empty string of at most 128 chars matching /^[a-zA-Z0-9._-]+$/. Anything else throws this error, protecting the Map from untrusted or malformed renderer input.

Source

Thrown at apps/desktop/electron/main.mjs:1078

const browserPanel = createBrowserPanel({
  remoteDebugPort,
  getWindow: () => mainWindow,
  onDeepLink: (urls) => queueDeepLinks(urls),
});

const workspaceStore = createWorkspaceStore({
  app,
  defaultDenBaseUrl: DEFAULT_DEN_BASE_URL,
  defaultRequireSignin: DEFAULT_DESKTOP_REQUIRE_SIGNIN,
  forceRequireSignin: FORCE_DESKTOP_REQUIRE_SIGNIN,
});

const activeDesktopTransfers = new Map();

function desktopTransferKey(event, transferId) {
  const normalizedId = typeof transferId === "string" ? transferId.trim() : "";
  if (!normalizedId || normalizedId.length > 128 || !/^[a-zA-Z0-9._-]+$/.test(normalizedId)) {
    throw new Error("A valid transferId is required.");
  }
  return `${event.sender.id}:${normalizedId}`;
}

async function runDesktopTransfer(event, input, operation) {
  const key = desktopTransferKey(event, input?.transferId);
  if (activeDesktopTransfers.has(key)) throw new Error("transferId is already active.");
  const controller = new AbortController();
  const abort = () => controller.abort();
  activeDesktopTransfers.set(key, controller);
  event.sender.once("destroyed", abort);
  try {
    // Both authorities come from app-owned state in userData; workspace-
    // writable configuration must never widen where a transfer may write.
    const [authorizedRoots, allowedUrlPrefixes] = await Promise.all([
      workspaceStore.listLocalWorkspacePaths(),
      workspaceStore.listRemoteWorkspaceUrlPrefixes(),
    ]);

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Generate transferId in the renderer as crypto.randomUUID() (matches the allowed charset).
  2. Trim and validate the id client-side before invoking the IPC call.
  3. Ensure input is an object: pass { transferId } rather than the raw string.
  4. Check for version skew — reload/rebuild the renderer so it matches the main-process contract.

Example fix

// before
invoke('desktop:transfer', { transferId: `${file.name} (${i})` });
// after
const transferId = crypto.randomUUID();
invoke('desktop:transfer', { transferId });
Defensive patterns

Strategy: validation

Validate before calling

const TRANSFER_ID_RE = /^[a-zA-Z0-9._-]{1,128}$/;
if (typeof transferId !== 'string' || !TRANSFER_ID_RE.test(transferId.trim())) {
  throw new TypeError('transferId must be 1-128 chars of [a-zA-Z0-9._-]');
}

Type guard

function isValidTransferId(v) {
  return typeof v === 'string' && v.length > 0 && v.length <= 128 && /^[a-zA-Z0-9._-]+$/.test(v);
}

Try / catch

try {
  await runDesktopTransfer(event, input, op);
} catch (err) {
  if (String(err.message) === 'A valid transferId is required.') {
    console.error('Bad transferId from renderer:', input?.transferId);
  } else throw err;
}

Prevention

When it happens

Trigger: Any IPC desktop transfer call (runDesktopTransfer) whose input.transferId is missing, not a string, empty/whitespace, longer than 128 chars, or contains characters outside [a-zA-Z0-9._-] (e.g. spaces, slashes, unicode).

Common situations: Renderer sending undefined transferId after a refactor; generating ids with characters like ':' or '/'; pasting a UUID with braces; corrupted IPC payload from an older renderer bundle.

Related errors


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