HeyPuter/puter · error · HttpError

access_denied

access_denied

Error message

Write access denied for destination

What it means

#assertWriteAccess runs an ACL check (this.services.acl.check with 'write') against the parent directory of the output path, resolving ancestors via fsService.getAncestorChain. If the actor lacks write permission on that directory, generate() throws HTTP 403 access_denied before spending credits. This enforces per-user filesystem permissions on generated-image output.

Source

Thrown at src/backend/drivers/ai-image/ImageGenerationDriver.ts:477

        let ancestorsCache: Promise<
            Array<{ uid: string; path: string }>
        > | null = null;
        const canWrite = await this.services.acl.check(
            actor,
            {
                path: pathToCheck,
                resolveAncestors() {
                    if (!ancestorsCache) {
                        ancestorsCache =
                            fsService.getAncestorChain(pathToCheck);
                    }
                    return ancestorsCache;
                },
            },
            'write',
        );
        if (!canWrite) {
            throw new HttpError(403, 'Write access denied for destination', {
                legacyCode: 'access_denied',
            });
        }
    }

    #resolveModel(modelId: string, provider?: string): IImageModel | null {
        const models = this.#modelIdMap[modelId];
        if (!models || models.length === 0) return null;
        if (!provider) return models[0];
        return models.find((m) => m.provider === provider) ?? models[0];
    }
}

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Write into a directory the actor owns or has been granted write access to.
  2. Pre-check the destination with the ACL/fs layer before calling generate().
  3. Omit puter_output_path and handle the returned URL yourself if you don't need FS persistence.

Example fix

// before
await driver.generate({ prompt, puter_output_path: '/someone-else/x.png' });
// after
await driver.generate({ prompt, puter_output_path: `/${actor.user.username}/Pictures/x.png` });
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check write permission on the destination parent before generating.
const parent = posix.dirname(posix.normalize(puter_outputPath));
const ok = await aclService.check(actor, { path: parent }, 'write');
if (!ok) throw new Error(`no write access to ${parent}`);

Try / catch

try {
  await driver.generate({ prompt, puter_output_path });
} catch (e) {
  if (e?.code === 'access_denied') { promptChooseWritableFolder(); return; }
  throw e;
}

Prevention

When it happens

Trigger: The authenticated user does not have write permission on the directory named by puter_output_path — another user's folder, a read-only/app directory, or a path the user can't reach.

Common situations: Trying to save generated images into someone else's dir, a shared/readonly folder, or a path that doesn't exist and isn't creatable under the user's space.

Understand the failure class

Related errors


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