HeyPuter/puter · error · HttpError

cannot_write_to_root

cannot_write_to_root

Error message

Cannot write to root path

What it means

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.

Source

Thrown at src/backend/controllers/fs/FSController.ts:2333

    ): Promise<void> {
        const actor = req.actor;
        if (!actor) {
            throw new HttpError(401, 'Unauthorized', {
                legacyCode: 'unauthorized',
            });
        }
        const normalizedFileMetadata = options?.pathAlreadyNormalized
            ? fileMetadata
            : this.#normalizeFileMetadataPath(req, fileMetadata);
        if (!normalizedFileMetadata) {
            throw new HttpError(400, 'Missing path', {
                legacyCode: 'bad_request',
            });
        }

        const targetPath = normalizedFileMetadata.path;
        if (targetPath === '/') {
            throw new HttpError(400, 'Cannot write to root path', {
                legacyCode: 'cannot_write_to_root',
            });
        }
        const parentPath = pathPosix.dirname(targetPath);
        if (parentPath === '/') {
            throw new HttpError(400, 'Cannot write to root path', {
                legacyCode: 'cannot_write_to_root',
            });
        }

        let pathToCheck = parentPath;
        if (Boolean(normalizedFileMetadata.overwrite)) {
            const destinationExists =
                await this.services.fs.entryExistsByPath(targetPath);
            if (destinationExists) {
                pathToCheck = targetPath;
            }
        }

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Write to a concrete folder under the user's home, e.g. `/<username>/file.txt`.
  2. Validate the resolved path is not '/' before sending; if it is, prompt the user for a real destination.
  3. Audit the path-building logic — a root result almost always means an empty basename upstream.
  4. For batch writes, filter or reject '/' items before submission.

Example fix

// before
{ path: '/' + (name || '') }  // collapses to '/' when name empty
// after
if (!name) throw new Error('name required');
{ path: `/${username}/${name}` }
Defensive patterns

Strategy: validation

Validate before calling

function assertNotRoot(path) {
  if (path === '/') throw new Error('Cannot write to root — choose a subfolder');
  return path;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12). Data as JSON: /api/errors/d07d918445131d56. Report an issue: GitHub.