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

  1. Refresh the site list and retry with the current UUID.
  2. Confirm the authenticated user owns the site — check the list-sites response for the UUID.
  3. If the UUID is correct and owned, check server logs for store errors or DB connectivity issues.
  4. 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

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.