HeyPuter/puter · error · HttpError
subject_does_not_exist
subject_does_not_exist
Error message
Entry not found: path=${path} What it means
`assertAccess` masks ACL denials for app-under-user actors as 404 `subject_does_not_exist`. The deliberate intent (documented inline) is to avoid leaking the existence of another user's or another app's private files through a distinct error code. So an app that lacks read/write on a path gets 'not found' even when the entry exists.
Source
Thrown at src/backend/controllers/fs/legacyFsHelpers.ts:206
status?: unknown;
message?: unknown;
fields?: { code?: unknown };
};
const status = Number(safe?.status);
const message =
typeof safe?.message === 'string' && safe.message.length > 0
? safe.message
: 'Access denied';
const code =
typeof safe?.fields?.code === 'string' ? safe.fields.code : undefined;
const legacyCode = code === 'forbidden' ? 'access_denied' : code;
// App-under-user actors see denials as 404 "subject_does_not_exist"
// so existence of a sibling user's / other-app's files isn't leaked
// through the error code. User-actor denials keep the real 403.
if (isAppActor(actor)) {
throw new HttpError(404, `Entry not found: path=${path}`, {
legacyCode: 'subject_does_not_exist',
});
}
if (status === 404) {
throw new HttpError(404, message, {
...(legacyCode ? { legacyCode } : {}),
});
}
throw new HttpError(403, message, {
legacyCode: legacyCode ?? 'access_denied',
});
}
/**
* Authorize creation of a new entry at `targetPath`. The standard rule is write
* on the parent, but we also allow it when the actor has explicit write on the
* target itself — this covers an app creating its ownView on GitHub (pinned to 908ec23eda)
Solutions
- Grant the app explicit access (share or ACL rule) to the path, then retry.
- Do not infer from this 404 that the entry is absent — for app actors it can mean 'forbidden'.
- Verify as the owning user that the path exists before debugging app access.
Example fix
// before — app has no grant on the file
await appClient.fs.read('/alice/secret.txt'); // -> 404 subject_does_not_exist
// after — owner grants the app read access first
await ownerClient.acl.grant({ subject: '/alice/secret.txt', app: appUid, mode: 'read' });
await appClient.fs.read('/alice/secret.txt'); Defensive patterns
Strategy: try-catch
Type guard
function isAppActorDenied404(e) { return e?.status === 404 && e?.code === 'subject_does_not_exist'; } Try / catch
try { await appClient.fs.read(path); }
catch (e) {
if (isAppActorDenied404(e)) { await grantAccess(path, appUid); return retry(); }
throw e;
} Prevention
- Grant the app explicit ACL access before it touches the path.
- Do not treat this 404 as 'absent' for app actors — it often means forbidden.
- Verify existence as the owning user when debugging.
When it happens
Trigger: An app-scoped actor attempts to read/write a path it has no ACL grant on — including paths that genuinely exist but are owned by a sibling user or a different app's AppData subtree.
Common situations: App tries to access a file outside its granted scope without a share; missing share/permission grant to the app; app assumes a path exists and gets a misleading 404.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/ecdcff59a3886b3b.
Report an issue: GitHub.