{"record":{"id":"bd57dc90b39ea5af","repo":"microsoft/typescript-go","slug":"cannot-create-directory-a-file-already-exists-at","errorCode":null,"errorMessage":"Cannot create directory: a file already exists at \"/${segments.join(\"/\")}\"","messagePattern":"Cannot create directory: a file already exists at \"/(.+?)\"","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"_packages/native-preview/src/api/fs.ts","lineNumber":86,"sourceCode":"                return undefined;\r\n            }\r\n            const child: VNode = current.children[segment];\r\n            if (!child) {\r\n                return undefined;\r\n            }\r\n            current = child;\r\n        }\r\n        return current;\r\n    }\r\n\r\n    function ensureDirectory(segments: string[]): VDirectory {\r\n        let current: VDirectory = root;\r\n        for (const segment of segments) {\r\n            if (!current.children[segment]) {\r\n                current.children[segment] = { type: \"directory\", children: {} };\r\n            }\r\n            else if (current.children[segment].type !== \"directory\") {\r\n                throw new Error(`Cannot create directory: a file already exists at \"/${segments.join(\"/\")}\"`);\r\n            }\r\n            current = current.children[segment] as VDirectory;\r\n        }\r\n        return current;\r\n    }\r\n\r\n    function addToTree(path: string): void {\r\n        const segments = getPathComponents(path).slice(1);\r\n        if (segments.length === 0) {\r\n            throw new Error(`Invalid file path: \"${path}\"`);\r\n        }\r\n        const filename = segments.pop()!;\r\n        const dirNode = ensureDirectory(segments);\r\n        dirNode.children[filename] = { type: \"file\" };\r\n    }\r\n\r\n    function writeFile(path: string, data: string): void {\r\n        content[path] = data;\r","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/_packages/native-preview/src/api/fs.ts#L68-L104","documentation":"Thrown by ensureDirectory inside createVirtualFileSystem when inserting a file whose path needs a directory segment that is already registered as a file. The virtual FS is a tree built from the flat files map (and later writeFile callbacks from the server); if one path is both a file and a prefix of another path (e.g., \"/a\" and \"/a/b.ts\"), the tree cannot represent it and construction/write fails.","triggerScenarios":"Passing a files map to createVirtualFileSystem containing both \"/a\" (file) and \"/a/b.ts\"; the Go server calling the writeFile callback with a path whose parent collides with an existing virtual file; inconsistent casing or separator styles (Windows backslashes) creating near-duplicate entries that collapse into collisions.","commonSituations":"In-memory test fixtures generated from real directory listings that include extensionless files doubling as directories; bundling virtual tarball-like inputs where a file and folder share a name; path strings built by joining user input without normalization.","solutions":["Sanitize the files map before creating the virtual FS: remove entries whose path is also a prefix of another entry","Normalize keys to absolute POSIX-style paths (forward slashes) so casing/separator differences do not create collisions","When handling server writeFile callbacks, catch this error and reject/rename the conflicting virtual file deliberately","Validate your fixture generator so directories and files never share the same path"],"exampleFix":"// before\nconst vfs = createVirtualFileSystem({\n    \"/project/util\": \"...\",      // file\n    \"/project/util/index.ts\": \"...\", // util as directory -> throws\n});\n\n// after\nconst vfs = createVirtualFileSystem({\n    \"/project/util.ts\": \"...\",\n    \"/project/util/index.ts\": \"...\",\n});","handlingStrategy":"validation","validationCode":"// Reject file/directory path collisions before building the virtual FS\nfunction hasPathCollision(files: Record<string, string>): string | undefined {\n    const paths = Object.keys(files).map(p => p.replace(/\\\\/g, '/')).sort();\n    for (let i = 0; i < paths.length - 1; i++) {\n        if (paths[i + 1].startsWith(paths[i].endsWith('/') ? paths[i] : paths[i] + '/')) return paths[i];\n    }\n    return undefined;\n}\nconst collision = hasPathCollision(files);\nif (collision) throw new Error(`'${collision}' is both a file and a directory prefix`);\nconst vfs = createVirtualFileSystem(files);","typeGuard":null,"tryCatchPattern":"try {\n    createVirtualFileSystem(files);\n} catch (e) {\n    if (e instanceof Error && e.message.includes('a file already exists at')) {\n        // report the offending fixture entry and fix the data\n        console.error('Virtual FS collision:', e.message);\n    } else throw e;\n}","preventionTips":["Normalize all virtual paths to absolute POSIX form with forward slashes","Generate fixtures from a tree structure, not a flat map, so collisions are impossible","Validate server writeFile callback paths for parent collisions before inserting"],"tags":["virtual-file-system","api","paths","validation","native-preview"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}