{"record":{"id":"7e01b8fe9ecb833d","repo":"different-ai/openwork","slug":"invalid-staging","errorCode":"invalid-staging","errorMessage":"Staging directory must be absolute.","messagePattern":"Staging directory must be absolute\\.","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/binary-transfer.mjs","lineNumber":271,"sourceCode":"// Downloads stream into a private staging file inside an app-owned directory\n// first, and only a fully successful download is placed into the workspace.\n// The staging directory sits outside every authorized workspace root, so a\n// process with workspace write access cannot swap its parents; its cleanup is\n// the only path-based removal this module performs. Inside the workspace the\n// destination is created exclusively, verified by device and inode, written\n// through the verified handle, and never unlinked by path: failure cleanup\n// truncates through the handle instead.\nexport async function downloadBinaryToPath(input, options) {\n  const url = remoteUrl(input?.url, options?.allowedUrlPrefixes);\n  const destination = await resolveAuthorizedPath(input?.destinationPath, options?.authorizedRoots);\n  const destinationPath = destination.path;\n  const maxBytes = boundedByteCount(input?.maxBytes, \"Maximum download size\", { allowUndefined: true });\n  const headers = boundedRecord(input?.headers, \"Headers\", MAX_HEADERS);\n  const method = boundedString(input?.method ?? \"GET\", \"Method\", { required: true, maxLength: 16 });\n  const signal = timeoutSignal(input?.timeoutMs, options?.signal);\n  const stagingDir = boundedString(options?.stagingDir, \"Staging directory\", { required: true, maxLength: 32_768 });\n  if (!path.isAbsolute(stagingDir)) {\n    throw transferError(\"Staging directory must be absolute.\", \"invalid-staging\");\n  }\n  let stagingPath;\n  let stagingFile;\n  let destinationFile;\n  try {\n    const response = await options.fetcher(url, {\n      method,\n      headers,\n      credentials: \"omit\",\n      cache: \"no-store\",\n      // The endpoint allowlist covers only the initial URL, so a redirect\n      // must never be followed to an unvalidated destination.\n      redirect: \"error\",\n      signal,\n    });\n    if (!response.ok) {\n      return { ...responseMetadata(response), body: await responseText(response), path: null, bytes: 0 };\n    }","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/binary-transfer.mjs#L253-L289","documentation":"downloadBinaryToPath downloads a binary through a bounded fetch pipeline and stages it in options.stagingDir before moving it into place. As part of input hardening it requires stagingDir to be a non-empty, length-bounded, ABSOLUTE path; a relative path throws a transferError with code invalid-staging.","triggerScenarios":"Calling downloadBinaryToPath with options.stagingDir set to a relative path such as \"tmp/staging\" or \".cache\" instead of an absolute filesystem path.","commonSituations":"Reconstructing the staging path with a relative CWD assumption; config files storing relative paths; tests running with a different working directory than the caller expected; platform code assuming forward-slash relative temp dirs.","solutions":["Resolve the staging directory to an absolute path with path.resolve()/path.join(os.tmpdir(), ...) before calling.","Store absolute paths in configuration, or resolve them at call time against the app's data directory.","Ensure the directory also exists (and is writable) — create it with fs.mkdir(recursive: true) beforehand.","Check the options object is being passed with the right key (options.stagingDir, not input)."],"exampleFix":"// before\nawait downloadBinaryToPath(url, dest, { stagingDir: \"tmp/staging\" })\n// after\nimport path from \"node:path\";\nawait downloadBinaryToPath(url, dest, { stagingDir: path.resolve(appDataDir, \"staging\") })","handlingStrategy":"validation","validationCode":"import path from \"node:path\";\nconst stagingDir = path.resolve(candidateDir);\nif (!path.isAbsolute(stagingDir)) throw new Error('stagingDir must be absolute');\nawait fs.mkdir(stagingDir, { recursive: true });","typeGuard":"function isAbsoluteDir(p: unknown): p is string { return typeof p === 'string' && p.length > 0 && path.isAbsolute(p) }","tryCatchPattern":"try { await downloadBinaryToPath(url, dest, { stagingDir }) } catch (e) { if (e.code === 'invalid-staging') console.error('stagingDir must be absolute, got:', stagingDir); }","preventionTips":["Always build staging paths with path.resolve/path.join against a known absolute base","Never store relative staging paths in configuration","Create the staging directory before downloading","Pass stagingDir in options, not the input object"],"tags":["filesystem","path-validation","binary-download"],"backgroundTag":"invalid-path","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}