{"record":{"id":"9c6b6ae1ddcbcae6","repo":"stablyai/orca","slug":"symlink-not-allowed-in-normalizerelativeuploadp","errorCode":null,"errorMessage":"Symlink not allowed in '${normalizeRelativeUploadPath(relative(rootPath, dirPath))}'","messagePattern":"Symlink not allowed in '(.+?)'","errorType":"exception","errorClass":"RuntimeUploadSymlinkError","httpStatus":null,"severity":"warning","filePath":"src/main/ipc/filesystem-mutations.ts","lineNumber":530,"sourceCode":"      return { sourcePath, status: 'skipped', reason: 'symlink' }\n    }\n    return {\n      sourcePath,\n      status: 'failed',\n      reason: error instanceof Error ? error.message : String(error)\n    }\n  }\n}\n\nasync function stageDirectoryEntries(rootPath: string): Promise<StagedExternalImportEntry[]> {\n  const entries: StagedExternalImportEntry[] = [{ relativePath: '', kind: 'directory' }]\n  let totalBytes = 0\n  const rootRealPath = await realpath(rootPath)\n\n  async function visit(dirPath: string): Promise<void> {\n    const dirStat = await lstat(dirPath)\n    if (dirStat.isSymbolicLink()) {\n      throw new RuntimeUploadSymlinkError(\n        `Symlink not allowed in '${normalizeRelativeUploadPath(relative(rootPath, dirPath))}'`\n      )\n    }\n    if (!dirStat.isDirectory()) {\n      throw new Error(\n        `Unsupported file type in '${normalizeRelativeUploadPath(relative(rootPath, dirPath))}'`\n      )\n    }\n    await assertRealPathInsideRoot(\n      rootRealPath,\n      dirPath,\n      normalizeRelativeUploadPath(relative(rootPath, dirPath))\n    )\n    const dirEntries = await readdir(dirPath, { withFileTypes: true })\n    for (const entry of dirEntries) {\n      const childPath = join(dirPath, entry.name)\n      const childRelativePath = normalizeRelativeUploadPath(relative(rootPath, childPath))\n      if (entry.isSymbolicLink()) {","sourceCodeStart":512,"sourceCodeEnd":548,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/filesystem-mutations.ts#L512-L548","documentation":"Thrown as RuntimeUploadSymlinkError by stageDirectoryEntries() when a visited directory path itself lstat()s as a symbolic link. stageDirectoryEntries is part of the runtime-upload staging path (reading files to base64 for remote/agent upload); symlinks are forbidden to prevent traversal and non-portable link semantics. The dedicated error class is caught one frame up in stageOneSourceForRuntimeUpload and converted into a { status: 'skipped', reason: 'symlink' } result, so the whole source is skipped rather than failing the batch.","triggerScenarios":"stageOneSourceForRuntimeUpload processes a directory source whose root or a visited subdirectory is a symlink; lstat reports the visited dirPath as isSymbolicLink() and RuntimeUploadSymlinkError is thrown.","commonSituations":"A dropped/uploaded directory tree that contains symlinked subfolders (common in monorepos, build output dirs, or linked packages); the user selected a top-level path that is itself a symlink to a directory.","solutions":["Expect the source to be reported as skipped; copy the real content into a non-symlinked directory and upload that.","Remove or un-symlink the offending directory in the source tree before staging.","Filter symlinked directories out of the upload selection in the UI."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-detect symlinked directories before runtime-upload staging\nimport { lstat } from 'node:fs/promises'\nasync function isSymlinkDir(p: string): Promise<boolean> {\n  try { return (await lstat(p)).isSymbolicLink() } catch { return false }\n}","typeGuard":null,"tryCatchPattern":"// stageOneSourceForRuntimeUpload already maps RuntimeUploadSymlinkError -> skipped;\n// callers should branch on the result status:\nconst res = await stageOneSourceForRuntimeUpload(src)\nif (res.status === 'skipped' && res.reason === 'symlink') {\n  // report to user; offer to upload the real target instead\n}","preventionTips":["Filter symlinked directories out of the upload selection in the UI.","Resolve symlinks to their real targets before staging if the content is needed.","Treat skipped/symlink results as expected, not as crashes."],"tags":["symlink","upload","runtime-upload","staging","skipped"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}