HeyPuter/puter · error · HttpError
bad_request
bad_request
Error message
Missing or invalid `site_uuid`
What it means
The `POST /delete-site` endpoint requires a `site_uuid` string in the JSON request body. The controller validates the field immediately and rejects with HTTP 400 if it is missing, not a string, or empty. This is a client-side input error — the request never reaches the store layer.
Source
Thrown at src/backend/controllers/hosting/HostingController.js:69
allowFullAccessToken: true,
requireVerified: true,
// Destructive, and pairs with the `subdomains:create`
// budget on the driver side.
rateLimit: {
scope: 'delete-site',
limit: 60,
window: 60_000,
key: 'user',
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',View on GitHub (pinned to 908ec23eda)
Solutions
- Ensure the request body is valid JSON with a `site_uuid` field set to the site's UUID string.
- Set the `Content-Type: application/json` header so Express parses the body.
- Confirm the UUID comes from a prior list-sites or create-site response, not a fabricated value.
- Add a client-side check that `site_uuid` is a non-empty string before posting.
Example fix
// before
await fetch('/api/delete-site', {
method: 'POST',
body: JSON.stringify({ uuid: site.id }), // wrong field name
});
// after
await fetch('/api/delete-site', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ site_uuid: site.uuid }),
}); Defensive patterns
Strategy: validation
Validate before calling
function isValidSiteUuid(v) {
return typeof v === 'string' && v.length > 0;
}
// before calling
if (!isValidSiteUuid(site.uuid)) {
throw new Error('Cannot delete: site UUID is missing');
}
await fetch('/api/delete-site', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ site_uuid: site.uuid }),
}); Type guard
/** @param {unknown} v @returns {v is string} */
function isNonEmptyString(v) {
return typeof v === 'string' && v.length > 0;
} Try / catch
try {
const res = await fetch('/api/delete-site', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ site_uuid }),
});
if (!res.ok) throw await res.json();
} catch (e) {
if (e.code === 'bad_request') {
console.error('site_uuid is missing or invalid:', e.message);
}
} Prevention
- Always send `Content-Type: application/json` for POST endpoints that read `req.body`.
- Derive `site_uuid` from a trusted list-sites response, not user input.
- Validate the UUID is a non-empty string before constructing the request.
When it happens
Trigger: Calling `POST /api/delete-site` with a body that omits `site_uuid`, passes a number/object/null, or passes an empty string. Also fires if the body is not parsed as JSON (wrong Content-Type) so `req.body` is undefined.
Common situations: Frontend sends the UUID from the wrong field name (e.g., `uuid` instead of `site_uuid`); the value is undefined because the site list row was null; a test harness posts an incomplete fixture.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/05a457632ad1a79b.
Report an issue: GitHub.