{"record":{"id":"5f570f9124ef1ecc","repo":"HeyPuter/puter","slug":"cannot-write-to-root-5f570f","errorCode":"cannot_write_to_root","errorMessage":"Cannot write to root path","messagePattern":"Cannot write to root path","errorType":"http","errorClass":"HttpError","httpStatus":400,"severity":"error","filePath":"src/backend/drivers/ai-image/ImageGenerationDriver.ts","lineNumber":446,"sourceCode":"        if (resolved === '~' || resolved.startsWith('~/')) {\n            resolved = `/${username}${resolved.slice(1)}`;\n        }\n        assertNormalized(resolved);\n        if (!resolved.startsWith('/')) {\n            resolved = `/${resolved}`;\n        }\n        if (resolved.length > 1 && resolved.endsWith('/')) {\n            resolved = resolved.slice(0, -1);\n        }\n        return resolved;\n    }\n\n    async #assertWriteAccess(\n        actor: Actor,\n        resolvedPath: string,\n    ): Promise<void> {\n        if (resolvedPath === '/') {\n            throw new HttpError(400, 'Cannot write to root path', {\n                legacyCode: 'cannot_write_to_root',\n            });\n        }\n        const parentPath = pathPosix.dirname(resolvedPath);\n        if (parentPath === '/') {\n            throw new HttpError(400, 'Cannot write to root path', {\n                legacyCode: 'cannot_write_to_root',\n            });\n        }\n\n        const pathToCheck = parentPath;\n        const fsService = this.services.fs;\n        let ancestorsCache: Promise<\n            Array<{ uid: string; path: string }>\n        > | null = null;\n        const canWrite = await this.services.acl.check(\n            actor,\n            {","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/HeyPuter/puter/blob/908ec23eda38526170322c3edf71ba45ecb1ca95/src/backend/drivers/ai-image/ImageGenerationDriver.ts#L428-L464","documentation":"In #assertWriteAccess, if the fully resolved puter_output_path is exactly '/', the driver refuses to write with HTTP 400 cannot_write_to_root. Writing the generated image over the filesystem root is forbidden.","triggerScenarios":"Caller passes puter_output_path that normalizes to '/' — e.g. '/', an empty string after normalization, or a path that collapses to root.","commonSituations":"Passing puter_output_path: '/' or '' by mistake; a path-templating bug that produces an empty/root path; UI default that accidentally resolves to root.","solutions":["Pass a concrete file path like '/Pictures/gen.png', never '/' or empty.","Validate puter_output_path is a non-root file path before calling generate()."],"exampleFix":"// before\nawait driver.generate({ prompt, puter_output_path: '/' });\n// after\nawait driver.generate({ prompt, puter_output_path: '/Pictures/gen.png' });","handlingStrategy":"validation","validationCode":"const posix = require('node:path').posix;\nconst norm = posix.normalize(puter_outputPath);\nif (norm === '/') throw new Error('cannot write generated image to root');","typeGuard":"function isNonRootFilePath(p) {\n  const n = require('node:path').posix.normalize(p ?? '');\n  return typeof p === 'string' && n !== '/' && n.trim() !== '';\n}","tryCatchPattern":null,"preventionTips":["Never pass '/' or '' as puter_output_path.","Validate the path is a concrete file under a directory before calling generate().","Default generated-image paths to a real folder like /Pictures."],"tags":["ai-image","filesystem","validation","path-traversal","bad-request"],"backgroundTag":null,"analyzedSha":"908ec23eda38526170322c3edf71ba45ecb1ca95","analyzedAt":"2026-08-12T20:53:15.911Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}