Fix GUI failed response handling

This commit is contained in:
Meta Vens 2026-04-29 22:40:14 +01:00
commit 334dcd1edf
3 changed files with 33 additions and 10 deletions

View file

@ -1122,7 +1122,7 @@ function endPan(pointerId: number | null = null): void {
async function loadRuntimeConfig(): Promise<void> {
const response = await fetch("/api/config");
if (!response.ok) {
throw new Error(await response.text());
throw new Error(await responseErrorMessage(response, "Failed to load runtime config."));
}
applyRuntimeConfig((await response.json()) as RuntimeConfig);
}
@ -1138,7 +1138,7 @@ async function refreshQueueSummary(): Promise<void> {
}
const response = await fetch("/api/queue");
if (!response.ok) {
throw new Error(await response.text());
throw new Error(await responseErrorMessage(response, "Failed to load queue summary."));
}
state.queueSummary = (await response.json()) as QueueSummary;
renderQueueSummary();
@ -1223,6 +1223,18 @@ function extractErrorDetail(payload: unknown, fallback: string): string {
return fallback;
}
async function responseErrorMessage(response: Response, fallback: string): Promise<string> {
const body = await response.text();
if (!body.trim()) {
return fallback;
}
try {
return extractErrorDetail(JSON.parse(body), fallback);
} catch {
return body;
}
}
async function cancelCurrentJob(options: { keepalive?: boolean; silent?: boolean } = {}): Promise<void> {
if (!state.jobId || !hasActiveJob()) {
return;
@ -1237,7 +1249,7 @@ async function cancelCurrentJob(options: { keepalive?: boolean; silent?: boolean
try {
const response = await request;
if (!response.ok) {
throw new Error(await response.text());
throw new Error(await responseErrorMessage(response, "Failed to cancel the current job."));
}
const payload = (await response.json()) as JobStateSnapshot;
applyJobSnapshot(payload);
@ -1573,8 +1585,7 @@ async function startRun(): Promise<void> {
body: buildFormData(),
});
if (!response.ok) {
const errorPayload = await response.json().catch(async () => ({ detail: await response.text() }));
throw new Error(extractErrorDetail(errorPayload, "Failed to start GUI job."));
throw new Error(await responseErrorMessage(response, "Failed to start GUI job."));
}
const payload = (await response.json()) as JobStateSnapshot;
state.jobId = payload.jobId;

View file

@ -887,7 +887,7 @@ function endPan(pointerId = null) {
async function loadRuntimeConfig() {
const response = await fetch("/api/config");
if (!response.ok) {
throw new Error(await response.text());
throw new Error(await responseErrorMessage(response, "Failed to load runtime config."));
}
applyRuntimeConfig((await response.json()));
}
@ -902,7 +902,7 @@ async function refreshQueueSummary() {
}
const response = await fetch("/api/queue");
if (!response.ok) {
throw new Error(await response.text());
throw new Error(await responseErrorMessage(response, "Failed to load queue summary."));
}
state.queueSummary = (await response.json());
renderQueueSummary();
@ -979,6 +979,18 @@ function extractErrorDetail(payload, fallback) {
}
return fallback;
}
async function responseErrorMessage(response, fallback) {
const body = await response.text();
if (!body.trim()) {
return fallback;
}
try {
return extractErrorDetail(JSON.parse(body), fallback);
}
catch {
return body;
}
}
async function cancelCurrentJob(options = {}) {
if (!state.jobId || !hasActiveJob()) {
return;
@ -993,7 +1005,7 @@ async function cancelCurrentJob(options = {}) {
try {
const response = await request;
if (!response.ok) {
throw new Error(await response.text());
throw new Error(await responseErrorMessage(response, "Failed to cancel the current job."));
}
const payload = (await response.json());
applyJobSnapshot(payload);
@ -1287,8 +1299,7 @@ async function startRun() {
body: buildFormData(),
});
if (!response.ok) {
const errorPayload = await response.json().catch(async () => ({ detail: await response.text() }));
throw new Error(extractErrorDetail(errorPayload, "Failed to start GUI job."));
throw new Error(await responseErrorMessage(response, "Failed to start GUI job."));
}
const payload = (await response.json());
state.jobId = payload.jobId;

View file

@ -25,3 +25,4 @@
{"ts": "2026-04-29T13:37:17+00:00", "type": "hosted", "status": "accepted", "note": "Hosted Repixelizer now enables Patreon alongside Discord. Patreon access policy is title-only: REPIXELIZER_ACCESS_PATREON_TIER_TITLE=Inner Sanctum is sent to Heimdall as patreon_membership_access during backend-callback OAuth start."}
{"ts": "2026-04-29T17:20:00+00:00", "type": "implementation", "status": "ok", "note": "Removed the hosted solver loss chart because the explicit solver's loss-like diagnostic is not a useful live objective; solver status now surfaces step, signal, grid alignment, and displacement terms instead."}
{"ts": "2026-04-29T17:29:40+00:00", "type": "implementation", "status": "ok", "note": "Hosted GUI now emits explicit service logs for queue saturation, job queue/start/completion/failure/cancel events, and wait/run/total seconds so load pressure shows up in journald instead of only in transient SSE state."}
{"ts": "2026-04-29T21:40:11+00:00", "type": "implementation", "status": "ok", "note": "Fixed GUI failed-response handling so submit/config/queue/cancel errors read the Response body once, preserving the real server error instead of throwing 'body stream already read'."}