can1357/oh-my-pi · error

out?.error ?? `save failed (${res.status})`

Error message

out?.error ?? `save failed (${res.status})`

What it means

putExperimentMeta PUTs experiment metadata to the metaharness server. If the response is not ok, it throws with the server's JSON `error` field when present, otherwise a generic "save failed (<status>)" message. This is the client-side surface for any server-side rejection of a metadata save.

Source

Thrown at packages/metaharness/src/web/app.tsx:262

function shortTask(task: string): string {
	const base = task.slice(task.lastIndexOf("/") + 1);
	const us = base.lastIndexOf("__");
	return us >= 0 ? base.slice(us + 2) : base;
}

/** PUT experiment metadata: goal and/or per-run label/note/role. */
async function putExperimentMeta(
	id: string,
	body: { goal?: string; runs?: Record<string, { role?: RunRole; note?: string; label?: string }> },
): Promise<void> {
	const res = await fetch(`/api/experiments/${encodeURIComponent(id)}`, {
		method: "PUT",
		headers: { "content-type": "application/json" },
		body: JSON.stringify(body),
	});
	if (!res.ok) {
		const out = (await res.json().catch(() => null)) as { error?: string } | null;
		throw new Error(out?.error ?? `save failed (${res.status})`);
	}
}

function RoleTag({ role }: { role: RunRole }) {
	return (
		<span
			className={`ml-2 rounded-full border px-1.5 text-[10px] ${role === "baseline" ? "border-sky-500 text-sky-400" : "border-emerald-600 text-emerald-400"}`}
		>
			{role}
		</span>
	);
}

// ── arm table sorting ────────────────────────────────────────────────────────

type SortKey = "arm" | "note" | "status" | "progress" | "eta" | "pass" | "cost" | "time";

interface SortSpec {

View on GitHub (pinned to 9690622007)

Solutions

  1. If the message contains a server error string, fix the metadata field it names
  2. Verify the experiment id still exists on the server (404 in the fallback message)
  3. Retry after refreshing, in case of a transient conflict or stale dashboard state
  4. Check server logs if the fallback 'save failed (status)' text appears — it means no error body was returned

Example fix

// before
await putExperimentMeta(id, { status: "bogus-status" });
// after: send a schema-valid value and handle failure
try {
  await putExperimentMeta(id, { status: "running" });
} catch (e) {
  showToast(`Save failed: ${e.message}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!validStatuses.has(body.status)) {
  throw new Error(`Invalid status "${body.status}" — rejected server-side before the PUT`);
}

Try / catch

try {
  await putExperimentMeta(id, body);
} catch (err) {
  showToast(/save failed \(\d+\)/.test(err.message)
    ? `Server rejected the save (${err.message}); refresh and retry`
    : `Save failed: ${err.message}`);
  await reloadExperiment(id); // resync with server state
}

Prevention

When it happens

Trigger: PUT to the experiment metadata endpoint returning non-OK: validation rejected by the server (no error body), 404 for a nonexistent experiment id, 409 conflict, or a crashed handler producing an empty error body.

Common situations: Editing metadata for an experiment that was deleted server-side; sending fields the server schema rejects; concurrent edits producing conflicts; server bug returning non-JSON error body.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/b8b588189c82c863. Report an issue: GitHub.