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
- If the message contains a server error string, fix the metadata field it names
- Verify the experiment id still exists on the server (404 in the fallback message)
- Retry after refreshing, in case of a transient conflict or stale dashboard state
- 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
- Validate edited fields against the server schema in the form before PUT
- Confirm the experiment still exists before editing (refresh list)
- Resync UI state from the server after any failed save
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
- ${url}: ${res.status}
- V2 remote compaction failed (${response.status} ${response.s
- No response body for V2 compaction streaming
- Request was aborted.
- Bedrock HTTP ${response.status}: ${errBody.slice(0, 1000)}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b8b588189c82c863.
Report an issue: GitHub.