HeyPuter/puter · error · HttpError
internal_error
internal_error
Error message
TURN credential generation failed
What it means
`POST /peer/generate-turn` called Cloudflare's TURN credential API and received a non-2xx response. The endpoint logs the status and body, then throws HTTP 500. This is an upstream failure — the local config is correct but Cloudflare rejected the request (bad token, invalid service ID, rate limit, or outage).
Source
Thrown at src/backend/controllers/peer/PeerController.ts:213
`https://rtc.live.cloudflare.com/v1/turn/keys/${serviceId}/credentials/generate-ice-servers`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${apiToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ ttl, customIdentifier }),
},
);
if (!cfRes.ok) {
const body = await cfRes.text();
console.warn(
'[peer] Cloudflare TURN credential generation failed',
cfRes.status,
body,
);
throw new HttpError(500, 'TURN credential generation failed', {
legacyCode: 'internal_error',
});
}
const data = (await cfRes.json()) as { iceServers?: unknown };
res.json({ ttl, iceServers: data.iceServers });
};
/**
* POST /turn/ingest-usage — internal-only TURN egress metering. an external
* service that knows the usage information from cloudflare will send it to
* us here. Meters each record directly against the owning user via
* `services.metering.incrementUsage` multiplied by turn:egress-bytes cost.
*/
#ingestUsage = async (req: Request, res: Response): Promise<void> => {
const cfg = this.config.peers;
if (!cfg || !cfg.internal_auth_secret) {
throw new HttpError(403, 'Forbidden', { legacyCode: 'forbidden' });View on GitHub (pinned to 908ec23eda)
Solutions
- Check backend logs for the Cloudflare status code and body printed by the `console.warn`.
- If 401/403: rotate the `cloudflare_turn_api_token` and verify it has permissions for the TURN service.
- If 404: verify `cloudflare_turn_service_id` matches an active TURN service in your Cloudflare account.
- If 429/5xx: retry after a brief delay — the failure is transient.
Defensive patterns
Strategy: retry
Try / catch
async function getTurnCredentials(maxRetries = 2) {
for (let i = 0; i <= maxRetries; i++) {
try {
const res = await fetch('/peer/generate-turn', { method: 'POST' });
if (res.ok) return await res.json();
if (res.status === 500) throw new Error('upstream failure');
throw await res.json(); // non-retryable
} catch (e) {
if (i === maxRetries) return null; // give up, use STUN fallback
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
} Prevention
- Retry transient Cloudflare failures with exponential backoff (1–2 retries).
- Check server logs for the Cloudflare status code to distinguish auth errors from outages.
- Rotate the Cloudflare API token if failures are persistent (401/403).
When it happens
Trigger: Cloudflare returned 4xx (invalid API token, wrong service ID, expired credentials) or 5xx (Cloudflare-side outage). The `cfRes.ok` check fails, the body is logged, and the error propagates to the client.
Common situations: The Cloudflare API token expired or was revoked; the service ID doesn't match the token's account; Cloudflare rate-limited the credential generation; transient Cloudflare API outage.
Related errors
AI-assisted analysis of HeyPuter/puter@908ec23eda (2026-08-12).
Data as JSON: /api/errors/cf7db87954cdc065.
Report an issue: GitHub.