diff --git a/service/pages.js b/service/pages.js
index a40e797..8620a0b 100644
--- a/service/pages.js
+++ b/service/pages.js
@@ -606,6 +606,8 @@ export function submit() {
};
const $ = (id) => document.getElementById(id);
+ const esc = (s) => String(s).replace(/[&<>"']/g, (c) => (
+ {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
const SUBS = {
1: 'the binary (up to 2 GiB — free below 100 MiB)',
2: 'name, publisher name, category, description',
@@ -713,7 +715,21 @@ export function submit() {
let json;
try {
const r = await fetch('/api/upload', { method: 'POST', body });
- json = await r.json();
+ // Read as text first — if the server returned an HTML error page
+ // (Caddy 413/502, a cached old vhost, etc.) the JSON.parse below
+ // throws "Unexpected token '<'" and the user has no idea what
+ // actually came back. Show them the HTTP status and the first
+ // chunk of the body instead.
+ const text = await r.text();
+ try {
+ json = JSON.parse(text);
+ } catch (parseErr) {
+ const preview = text.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 180);
+ status.innerHTML = '⚠️ Server returned non-JSON — HTTP ' + r.status +
+ ' (' + (r.headers.get('content-type') || 'no content-type') + ')' +
+ (preview ? '. Snippet: ' + esc(preview) + '' : '');
+ return;
+ }
} catch (err) {
status.innerHTML = '⚠️ Network error — ' + String(err?.message || err);
return;