HeyPuter/puter · error · HttpError
not_found
not_found
Error message
Site not found or not owned by you
What it means
`POST /delete-site` looked up the subdomain by UUID scoped to the calling user and found no matching row. The store call `subdomainStore.getByUuid(site_uuid, { userId })` returned null, meaning the site does not exist or belongs to a different user. This is an authorization-scoped 404 — the message intentionally does not distinguish the two cases to avoid enumeration.
Source
Thrown at src/backend/controllers/hosting/HostingController.js:78
bySubscription: {
[DEFAULT_FREE_SUBSCRIPTION]: 30,
[DEFAULT_TEMP_SUBSCRIPTION]: 10,
},
},
},
async (req, res) => {
const { site_uuid } = req.body ?? {};
if (!site_uuid || typeof site_uuid !== 'string') {
throw new HttpError(400, 'Missing or invalid `site_uuid`', {
legacyCode: 'bad_request',
});
}
const row = await this.subdomainStore.getByUuid(site_uuid, {
userId: req.actor.user.id,
});
if (!row) {
throw new HttpError(
404,
'Site not found or not owned by you',
{ legacyCode: 'not_found' },
);
}
if (row.protected) {
throw new HttpError(
403,
'Cannot delete a protected subdomain',
{ legacyCode: 'forbidden' },
);
}
await this.subdomainStore.deleteByUuid(site_uuid, {
userId: req.actor.user.id,
});
res.json({});View on GitHub (pinned to 908ec23eda)
Solutions
- Refresh the site list and retry with the current UUID.
- Confirm the authenticated user owns the site — check the list-sites response for the UUID.
- If the UUID is correct and owned, check server logs for store errors or DB connectivity issues.
- Treat 404 as success in idempotent delete flows (the site is already gone).
Example fix
// before — assume the site always exists
await api.call('delete-site', { site_uuid });
// after — handle 404 idempotently
try {
await api.call('delete-site', { site_uuid });
} catch (e) {
if (e.code !== 'not_found') throw e;
// site already gone — refresh UI
} Defensive patterns
Strategy: try-catch
Try / catch
try {
await api.call('delete-site', { site_uuid });
} catch (e) {
if (e.code === 'not_found') {
// Site already deleted or not owned — treat as success in idempotent flows
console.log('Site no longer exists; nothing to delete.');
} else {
throw e;
}
} Prevention
- Treat 404 on delete as success — the end state (site gone) is achieved.
- Refresh the site list before offering a delete action to avoid stale UUIDs.
- Pass only UUIDs from the authenticated user's own site list.
When it happens
Trigger: Passing a `site_uuid` that was deleted already; passing a UUID belonging to another user; passing a stale UUID from an old session; a race where the site was deleted between the list render and the delete click.
Common situations: UI shows a stale site list after another tab deleted the same site; user copy-pastes a UUID from someone else's deployment; the site was removed by an admin process.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/97d765bb34c44a94.
Report an issue: GitHub.