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
- Write into a directory the actor owns or has been granted write access to.
- Pre-check the destination with the ACL/fs layer before calling generate().
- 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
- Write only into directories the actor owns or is granted write access to.
- Pre-check the destination ACL before calling generate().
- Default output paths to the user's own home folder.
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/41d9a25ce905f048.
Report an issue: GitHub.