{"record":{"id":"f0653843265f98ae","repo":"different-ai/openwork","slug":"a-valid-transferid-is-required","errorCode":null,"errorMessage":"A valid transferId is required.","messagePattern":"A valid transferId is required\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/main.mjs","lineNumber":1078,"sourceCode":"const browserPanel = createBrowserPanel({\n  remoteDebugPort,\n  getWindow: () => mainWindow,\n  onDeepLink: (urls) => queueDeepLinks(urls),\n});\n\nconst workspaceStore = createWorkspaceStore({\n  app,\n  defaultDenBaseUrl: DEFAULT_DEN_BASE_URL,\n  defaultRequireSignin: DEFAULT_DESKTOP_REQUIRE_SIGNIN,\n  forceRequireSignin: FORCE_DESKTOP_REQUIRE_SIGNIN,\n});\n\nconst activeDesktopTransfers = new Map();\n\nfunction desktopTransferKey(event, transferId) {\n  const normalizedId = typeof transferId === \"string\" ? transferId.trim() : \"\";\n  if (!normalizedId || normalizedId.length > 128 || !/^[a-zA-Z0-9._-]+$/.test(normalizedId)) {\n    throw new Error(\"A valid transferId is required.\");\n  }\n  return `${event.sender.id}:${normalizedId}`;\n}\n\nasync function runDesktopTransfer(event, input, operation) {\n  const key = desktopTransferKey(event, input?.transferId);\n  if (activeDesktopTransfers.has(key)) throw new Error(\"transferId is already active.\");\n  const controller = new AbortController();\n  const abort = () => controller.abort();\n  activeDesktopTransfers.set(key, controller);\n  event.sender.once(\"destroyed\", abort);\n  try {\n    // Both authorities come from app-owned state in userData; workspace-\n    // writable configuration must never widen where a transfer may write.\n    const [authorizedRoots, allowedUrlPrefixes] = await Promise.all([\n      workspaceStore.listLocalWorkspacePaths(),\n      workspaceStore.listRemoteWorkspaceUrlPrefixes(),\n    ]);","sourceCodeStart":1060,"sourceCodeEnd":1096,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/main.mjs#L1060-L1096","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Generate transferId in the renderer as crypto.randomUUID() (matches the allowed charset).","Trim and validate the id client-side before invoking the IPC call.","Ensure input is an object: pass { transferId } rather than the raw string.","Check for version skew — reload/rebuild the renderer so it matches the main-process contract."],"exampleFix":"// before\ninvoke('desktop:transfer', { transferId: `${file.name} (${i})` });\n// after\nconst transferId = crypto.randomUUID();\ninvoke('desktop:transfer', { transferId });","handlingStrategy":"validation","validationCode":"const TRANSFER_ID_RE = /^[a-zA-Z0-9._-]{1,128}$/;\nif (typeof transferId !== 'string' || !TRANSFER_ID_RE.test(transferId.trim())) {\n  throw new TypeError('transferId must be 1-128 chars of [a-zA-Z0-9._-]');\n}","typeGuard":"function isValidTransferId(v) {\n  return typeof v === 'string' && v.length > 0 && v.length <= 128 && /^[a-zA-Z0-9._-]+$/.test(v);\n}","tryCatchPattern":"try {\n  await runDesktopTransfer(event, input, op);\n} catch (err) {\n  if (String(err.message) === 'A valid transferId is required.') {\n    console.error('Bad transferId from renderer:', input?.transferId);\n  } else throw err;\n}","preventionTips":["Generate ids with crypto.randomUUID().","Validate the id in the renderer before invoking IPC.","Never interpolate user/file names into transferId.","Mirror the main-process regex in renderer-side shared validation code."],"tags":["ipc","validation","electron"],"backgroundTag":"invalid-identifier-validation","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}