{"record":{"id":"4d8b0287c8132739","repo":"astral-sh/ruff","slug":"failed-to-save-playground-response-status","errorCode":null,"errorMessage":"Failed to save playground: ${response.status}","messagePattern":"Failed to save playground: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"playground/ruff/src/Editor/api.ts","lineNumber":30,"sourceCode":" */\nexport async function fetchPlayground(id: string): Promise<Playground | null> {\n  const response = await fetch(`${API_URL}/${encodeURIComponent(id)}`);\n  if (!response.ok) {\n    throw new Error(`Failed to fetch playground ${id}: ${response.status}`);\n  }\n  return await response.json();\n}\n\n/**\n * Save a playground and return its ID.\n */\nexport async function savePlayground(playground: Playground): Promise<string> {\n  const response = await fetch(API_URL, {\n    method: \"POST\",\n    body: JSON.stringify(playground),\n  });\n  if (!response.ok) {\n    throw new Error(`Failed to save playground: ${response.status}`);\n  }\n  return await response.text();\n}\n","sourceCodeStart":12,"sourceCodeEnd":34,"githubUrl":"https://github.com/astral-sh/ruff/blob/672bb4edf04c84f8b0753346359daee4057158f2/playground/ruff/src/Editor/api.ts#L12-L34","documentation":"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.","triggerScenarios":"POST to API_URL with a JSON body returning 5xx (worker failure, storage write error) or a 4xx (payload rejected by the route).","commonSituations":"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.","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"],"exampleFix":"// before\nconst id = await savePlayground(playground);\n\n// after\nlet id: string | null = null;\nfor (let attempt = 0; attempt < 2 && !id; attempt++) {\n  try {\n    id = await savePlayground(playground);\n  } catch (error) {\n    if (attempt === 1) console.error((error as Error).message);\n  }\n}","handlingStrategy":"try-catch","validationCode":"const payload = JSON.stringify(playground);\nif (payload.length > 1_000_000) {\n  throw new RangeError('Playground payload too large to save');\n}","typeGuard":"function isSavePayload(p: Playground): boolean {\n  return p.pythonSource.length > 0 && p.settingsSource.length >= 0;\n}","tryCatchPattern":"try {\n  return await savePlayground(playground);\n} catch (error) {\n  console.error(`Save failed: ${(error as Error).message}`);\n  return null; // caller keeps local state and may retry\n}","preventionTips":["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"],"tags":["network","http","playground","typescript"],"backgroundTag":null,"analyzedSha":"672bb4edf04c84f8b0753346359daee4057158f2","analyzedAt":"2026-08-16T08:54:05.464Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}