astral-sh/ruff · error · Error
Failed to save playground: ${response.status}
Error message
Failed to save playground: ${response.status} What it means
Thrown by the Ruff playground's savePlayground when the POST to the share API returns a non-2xx status. The request reached the Cloudflare worker (https://api.astral-1ad.workers.dev in prod, http://localhost:8787 in dev) but it refused or failed to persist the snippet.
Source
Thrown at playground/ruff/src/Editor/api.ts:30
*/
export async function fetchPlayground(id: string): Promise<Playground | null> {
const response = await fetch(`${API_URL}/${encodeURIComponent(id)}`);
if (!response.ok) {
throw new Error(`Failed to fetch playground ${id}: ${response.status}`);
}
return await response.json();
}
/**
* Save a playground and return its ID.
*/
export async function savePlayground(playground: Playground): Promise<string> {
const response = await fetch(API_URL, {
method: "POST",
body: JSON.stringify(playground),
});
if (!response.ok) {
throw new Error(`Failed to save playground: ${response.status}`);
}
return await response.text();
}
View on GitHub (pinned to 672bb4edf0)
Solutions
- Check response.status in browser devtools to distinguish 4xx (payload problem) from 5xx (server problem)
- Retry after a short delay; transient worker/storage errors usually clear
- In local dev, confirm the worker on localhost:8787 is running the current share API and reload
- If 5xx persists, reduce the snippet size or report the outage; keep a local copy of the source
Example fix
// before
const id = await savePlayground(playground);
// after
let id: string | null = null;
for (let attempt = 0; attempt < 2 && !id; attempt++) {
try {
id = await savePlayground(playground);
} catch (error) {
if (attempt === 1) console.error((error as Error).message);
}
} Defensive patterns
Strategy: try-catch
Validate before calling
const payload = JSON.stringify(playground);
if (payload.length > 1_000_000) {
throw new RangeError('Playground payload too large to save');
} Type guard
function isSavePayload(p: Playground): boolean {
return p.pythonSource.length > 0 && p.settingsSource.length >= 0;
} Try / catch
try {
return await savePlayground(playground);
} catch (error) {
console.error(`Save failed: ${(error as Error).message}`);
return null; // caller keeps local state and may retry
} Prevention
- Keep unsaved source in local storage so a failed save never loses work
- Disable or debounce the share button while a save is in flight
- Surface save failures to the user with a retry action
When it happens
Trigger: POST to API_URL with a JSON body returning 5xx (worker failure, storage write error) or a 4xx (payload rejected by the route).
Common situations: Sharing from the playground during a worker outage or deployment window; oversized settings/source payload; local dev where the worker is answering but not the share route.
Related errors
- Failed to fetch playground ${id}: ${response.status}
- Failed to fetch playground ${id}: ${response.status}
- Failed to save playground: ${response.status}
- Git fetch of commit {} failed: {}
- Git clone failed: {}
AI-assisted analysis of astral-sh/ruff@672bb4edf0 (2026-08-16).
Data as JSON: /api/errors/4d8b0287c8132739.
Report an issue: GitHub.