Handle oversize upload errors cleanly

This commit is contained in:
Meta Vens 2026-04-29 22:43:02 +01:00
commit e0025b4cbf
3 changed files with 47 additions and 2 deletions

View file

@ -1568,10 +1568,21 @@ async function startRun(): Promise<void> {
setStage("idle", "Waiting for input", "Pick a file first. The machine is not clairvoyant.");
return;
}
const runtimeConfig = state.runtimeConfig;
const uploadLimit = runtimeConfig?.limits.maxUploadBytes;
if (uploadLimit !== undefined && state.file.size > uploadLimit) {
setJobState("failed");
setStage(
"failed",
"File too large",
`${state.file.name} is ${formatByteLimit(state.file.size)}. Hosted uploads are capped at ${formatByteLimit(uploadLimit)}.`,
);
addLog("Failure", "Selected file is over the hosted upload limit.");
return;
}
resetRunArtifacts({ preserveSourceImage: true });
renderEventLog();
runButton.disabled = true;
const runtimeConfig = state.runtimeConfig;
const maxSteps = runtimeConfig?.limits.maxSteps ?? 48;
const defaultSteps = runtimeConfig?.limits.defaultSteps ?? 48;
state.solverStepBudget = Math.max(0, Math.min(maxSteps, parseOptionalInteger(stepsInput) ?? defaultSteps));
@ -1604,6 +1615,21 @@ async function startRun(): Promise<void> {
}
async function acceptFile(file: File): Promise<void> {
const uploadLimit = state.runtimeConfig?.limits.maxUploadBytes;
if (uploadLimit !== undefined && file.size > uploadLimit) {
state.file = null;
resetRunArtifacts();
dropzoneLabel.textContent = "Choose image";
setJobState("failed");
setStage(
"failed",
"File too large",
`${file.name} is ${formatByteLimit(file.size)}. Hosted uploads are capped at ${formatByteLimit(uploadLimit)}.`,
);
addLog("Failure", "Selected file is over the hosted upload limit.");
await renderEverything();
return;
}
state.file = file;
resetRunArtifacts();
dropzoneLabel.textContent = file.name;

View file

@ -1282,10 +1282,17 @@ async function startRun() {
setStage("idle", "Waiting for input", "Pick a file first. The machine is not clairvoyant.");
return;
}
const runtimeConfig = state.runtimeConfig;
const uploadLimit = runtimeConfig?.limits.maxUploadBytes;
if (uploadLimit !== undefined && state.file.size > uploadLimit) {
setJobState("failed");
setStage("failed", "File too large", `${state.file.name} is ${formatByteLimit(state.file.size)}. Hosted uploads are capped at ${formatByteLimit(uploadLimit)}.`);
addLog("Failure", "Selected file is over the hosted upload limit.");
return;
}
resetRunArtifacts({ preserveSourceImage: true });
renderEventLog();
runButton.disabled = true;
const runtimeConfig = state.runtimeConfig;
const maxSteps = runtimeConfig?.limits.maxSteps ?? 48;
const defaultSteps = runtimeConfig?.limits.defaultSteps ?? 48;
state.solverStepBudget = Math.max(0, Math.min(maxSteps, parseOptionalInteger(stepsInput) ?? defaultSteps));
@ -1318,6 +1325,17 @@ async function startRun() {
}
}
async function acceptFile(file) {
const uploadLimit = state.runtimeConfig?.limits.maxUploadBytes;
if (uploadLimit !== undefined && file.size > uploadLimit) {
state.file = null;
resetRunArtifacts();
dropzoneLabel.textContent = "Choose image";
setJobState("failed");
setStage("failed", "File too large", `${file.name} is ${formatByteLimit(file.size)}. Hosted uploads are capped at ${formatByteLimit(uploadLimit)}.`);
addLog("Failure", "Selected file is over the hosted upload limit.");
await renderEverything();
return;
}
state.file = file;
resetRunArtifacts();
dropzoneLabel.textContent = file.name;

View file

@ -26,3 +26,4 @@
{"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'."}
{"ts": "2026-04-29T21:42:58+00:00", "type": "implementation", "status": "ok", "note": "Oversize uploads were hitting nginx 413 before FastAPI and the GUI masked the response with a double-read error; GUI now preflights file size and reads failed responses once so users see the actual upload limit."}