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 own

View on GitHub (pinned to 908ec23eda)

Solutions

  1. Grant the app explicit access (share or ACL rule) to the path, then retry.
  2. Do not infer from this 404 that the entry is absent — for app actors it can mean 'forbidden'.
  3. 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

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.