astral-sh/ruff · error · Error

Failed to fetch playground ${id}: ${response.status}

Error message

Failed to fetch playground ${id}: ${response.status}

What it means

Thrown by the Ruff playground's fetchPlayground when the backend answers a non-2xx HTTP status. Playgrounds are persisted on a Cloudflare worker (https://api.astral-1ad.workers.dev in production, http://localhost:8787 in local dev), so this error means the request reached a server but the server refused or failed it (note: response.json() is only called on success).

Source

Thrown at playground/ruff/src/Editor/api.ts:16

const API_URL = import.meta.env.PROD
  ? "https://api.astral-1ad.workers.dev"
  : "http://localhost:8787";

export type Playground = {
  pythonSource: string;
  settingsSource: string;
};

/**
 * Fetch a playground by ID.
 */
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

  1. Verify the playground ID in the URL is complete and unmodified
  2. Confirm the worker deployment is healthy (open the API URL directly and check for a sane response) and retry
  3. Ask the sender to re-share the snippet; the stored entry may have expired or never been saved
  4. In local dev, ensure the API worker (localhost:8787) is running before the web dev server calls it

Example fix

// before
const playground = await fetchPlayground(id); // may throw on 404/5xx

// after
try {
  const playground = await fetchPlayground(id);
} catch (error) {
  console.error((error as Error).message);
  // fall back to the default playground content
}
Defensive patterns

Strategy: try-catch

Validate before calling

const ID_RE = /^[A-Za-z0-9_-]{4,}$/;
if (!ID_RE.test(id)) {
  throw new RangeError(`Invalid playground id: ${id}`);
}

Type guard

function isPlayground(v: unknown): v is Playground {
  return (
    typeof v === 'object' && v !== null &&
    typeof (v as Playground).pythonSource === 'string' &&
    typeof (v as Playground).settingsSource === 'string'
  );
}

Try / catch

try {
  const playground = await fetchPlayground(id);
  if (!playground) return defaults();
  return playground;
} catch (error) {
  console.error(`Could not load shared playground: ${(error as Error).message}`);
  return defaults();
}

Prevention

When it happens

Trigger: GET `${API_URL}/${encodeURIComponent(id)}` returning 404 for an unknown, mistyped, or expired playground ID, or 5xx when the worker itself is failing.

Common situations: Opening an old share link whose snippet expired server-side; hand-editing or truncating the ID in a URL; a partially deployed or broken worker.

Related errors


AI-assisted analysis of astral-sh/ruff@672bb4edf0 (2026-08-16). Data as JSON: /api/errors/a3dcb3f0697e69a6. Report an issue: GitHub.