# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: settings/students.spec.ts >> Settings: Students >> typing in the search writes the term back into the URL
- Location: src/tests/settings/students.spec.ts:211:7

# Error details

```
Error: Convex e2e:createUser failed (503): no available server

```

# Test source

```ts
  1  | import { resolveTarget } from '../framework/runtime.js';
  2  | 
  3  | /**
  4  |  * Call a Convex internal function via the HTTP API with admin key auth.
  5  |  *
  6  |  * Uses the `/api/run/<path>` endpoint, which is the same mechanism that
  7  |  * `npx convex run` and the masterev-backend sidecar use internally.
  8  |  * Internal functions are only callable with a valid admin key.
  9  |  *
  10 |  * Target resolution:
  11 |  *  - `E2E_TARGET=staging` (default): reads `E2E_CONVEX_URL` and
  12 |  *    `E2E_CONVEX_ADMIN_KEY` (per-customer admin key configured in CI
  13 |  *    or `.env`).
  14 |  *  - `E2E_TARGET=local`: reads `CONVEX_LOCAL_URL` (default
  15 |  *    `http://localhost:3210`) and `CONVEX_LOCAL_ADMIN_KEY` (single
  16 |  *    shared dev admin key).
  17 |  *
  18 |  * @param functionPath - Function path in "module:function" format (e.g., 'e2e:createUser'),
  19 |  *   automatically converted to slash format for the HTTP API
  20 |  * @param args - Arguments to pass to the function
  21 |  * @returns Parsed result from the function
  22 |  */
  23 | export async function convexRun<T = unknown>(
  24 |   functionPath: string,
  25 |   args: Record<string, unknown> = {}
  26 | ): Promise<T> {
  27 |   const target = resolveTarget();
  28 |   const convexUrl =
  29 |     target === 'local'
  30 |       ? process.env.CONVEX_LOCAL_URL ?? 'http://localhost:3210'
  31 |       : process.env.E2E_CONVEX_URL;
  32 |   const adminKey =
  33 |     target === 'local'
  34 |       ? process.env.CONVEX_LOCAL_ADMIN_KEY
  35 |       : process.env.E2E_CONVEX_ADMIN_KEY;
  36 | 
  37 |   if (!convexUrl) {
  38 |     throw new Error(
  39 |       target === 'local'
  40 |         ? 'CONVEX_LOCAL_URL is unset and the localhost:3210 default was overridden to empty'
  41 |         : 'E2E_CONVEX_URL environment variable is required (E2E_TARGET=staging)'
  42 |     );
  43 |   }
  44 |   if (!adminKey) {
  45 |     throw new Error(
  46 |       target === 'local'
  47 |         ? 'CONVEX_LOCAL_ADMIN_KEY environment variable is required (E2E_TARGET=local)'
  48 |         : 'E2E_CONVEX_ADMIN_KEY environment variable is required (E2E_TARGET=staging)'
  49 |     );
  50 |   }
  51 | 
  52 |   // Convex HTTP API uses slash-separated paths: /api/run/module/function
  53 |   // CLI uses colon format (e2e:createUser), convert to slash (e2e/createUser)
  54 |   const apiPath = functionPath.replace(':', '/');
  55 |   const url = `${convexUrl}/api/run/${apiPath}`;
  56 | 
  57 |   // Retry on OptimisticConcurrencyControlFailure. Convex's aggregate
  58 |   // component (`btreeNode` table) serialises writes through a small
  59 |   // number of tree nodes, and parallel Playwright workers regularly
  60 |   // race on the application-count aggregate when they each call
  61 |   // `e2e:createConvexApplication`. The failure is transient and a
  62 |   // simple exponential-backoff retry resolves it. Non-OCC errors
  63 |   // surface immediately so genuine server bugs are not masked.
  64 |   const MAX_RETRIES = 5;
  65 |   let lastBody = '';
  66 |   let lastStatus = 0;
  67 |   for (let attempt = 0; attempt < MAX_RETRIES; attempt += 1) {
  68 |     const response = await fetch(url, {
  69 |       method: 'POST',
  70 |       headers: {
  71 |         'Content-Type': 'application/json',
  72 |         'Authorization': `Convex ${adminKey}`,
  73 |       },
  74 |       body: JSON.stringify({ args }),
  75 |     });
  76 | 
  77 |     if (response.ok) {
  78 |       const result = await response.json();
  79 |       return result.value as T;
  80 |     }
  81 | 
  82 |     lastStatus = response.status;
  83 |     lastBody = await response.text();
  84 |     const occRetryable =
  85 |       response.status === 503 &&
  86 |       lastBody.includes('OptimisticConcurrencyControlFailure');
  87 |     if (!occRetryable) break;
  88 |     const delayMs = 50 * Math.pow(2, attempt) + Math.floor(Math.random() * 50);
  89 |     await new Promise((resolve) => setTimeout(resolve, delayMs));
  90 |   }
> 91 |   throw new Error(
     |         ^ Error: Convex e2e:createUser failed (503): no available server
  92 |     `Convex ${functionPath} failed (${lastStatus}): ${lastBody}`
  93 |   );
  94 | }
  95 | 
```