{"record":{"id":"d07d918445131d56","repo":"HeyPuter/puter","slug":"cannot-write-to-root","errorCode":"cannot_write_to_root","errorMessage":"Cannot write to root path","messagePattern":"Cannot write to root path","errorType":"exception","errorClass":"HttpError","httpStatus":400,"severity":"error","filePath":"src/backend/controllers/fs/FSController.ts","lineNumber":2333,"sourceCode":"    ): Promise<void> {\n        const actor = req.actor;\n        if (!actor) {\n            throw new HttpError(401, 'Unauthorized', {\n                legacyCode: 'unauthorized',\n            });\n        }\n        const normalizedFileMetadata = options?.pathAlreadyNormalized\n            ? fileMetadata\n            : this.#normalizeFileMetadataPath(req, fileMetadata);\n        if (!normalizedFileMetadata) {\n            throw new HttpError(400, 'Missing path', {\n                legacyCode: 'bad_request',\n            });\n        }\n\n        const targetPath = normalizedFileMetadata.path;\n        if (targetPath === '/') {\n            throw new HttpError(400, 'Cannot write to root path', {\n                legacyCode: 'cannot_write_to_root',\n            });\n        }\n        const parentPath = pathPosix.dirname(targetPath);\n        if (parentPath === '/') {\n            throw new HttpError(400, 'Cannot write to root path', {\n                legacyCode: 'cannot_write_to_root',\n            });\n        }\n\n        let pathToCheck = parentPath;\n        if (Boolean(normalizedFileMetadata.overwrite)) {\n            const destinationExists =\n                await this.services.fs.entryExistsByPath(targetPath);\n            if (destinationExists) {\n                pathToCheck = targetPath;\n            }\n        }","sourceCodeStart":2315,"sourceCodeEnd":2351,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/controllers/fs/FSController.ts#L2315-L2351","documentation":"Thrown by FSController.#assertWriteAccess when the normalized target path is exactly `/`. Writing to the filesystem root is forbidden — root is the per-user mount namespace, not a writable location. This 400 (legacyCode `cannot_write_to_root`) fires before any ACL check, because no actor is ever permitted to write there.","triggerScenarios":"POST /write with `path: '/'`; a signWrite or batchWrite item whose path normalizes to `/`; a client that builds the path from a dirname that collapses to root (e.g. basename empty).","commonSituations":"Frontend letting the user save to the literal root; a path-join bug where dirname swallows the filename; default path constant set to '/'; copy/move destination computed as root.","solutions":["Write to a concrete folder under the user's home, e.g. `/<username>/file.txt`.","Validate the resolved path is not '/' before sending; if it is, prompt the user for a real destination.","Audit the path-building logic — a root result almost always means an empty basename upstream.","For batch writes, filter or reject '/' items before submission."],"exampleFix":"// before\n{ path: '/' + (name || '') }  // collapses to '/' when name empty\n// after\nif (!name) throw new Error('name required');\n{ path: `/${username}/${name}` }","handlingStrategy":"validation","validationCode":"function assertNotRoot(path) {\n  if (path === '/') throw new Error('Cannot write to root — choose a subfolder');\n  return path;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Reject '/' destinations in the UI before submit.","Audit path-join logic — a '/' result usually means an empty basename.","Default to a concrete user folder for saves."],"tags":["fs","validation","api","path","write","bad-request"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}