diff --git a/webui/index.html b/webui/index.html
index 9ab44aa8..66e9a9f2 100644
--- a/webui/index.html
+++ b/webui/index.html
@@ -592,7 +592,14 @@ function api(path,opts){
}
return fetch(path,opts).then(function(r){
if(r.status===401){showLogin();throw new Error("auth")}
- return r.json().then(function(j){
+ // An error response need not carry JSON — a bare 404 from handleNotFound
+ // has an empty body. Letting the parse failure escape would strip the HTTP
+ // status off the error and leave callers unable to tell "no such endpoint"
+ // from "the network dropped". Successful responses must still parse.
+ return r.json().catch(function(){
+ if(r.ok)throw new Error("unreadable reply from the node");
+ return {};
+ }).then(function(j){
if(!r.ok&&r.status!==202){
// Carry the HTTP status and any batch reqid so callers can tell a
// definite rejection (400/409/413) from an ambiguous network failure.
@@ -1936,6 +1943,9 @@ function cliRun(cmds){
cliPoll(reqid,cmds,0,status,0,0);
}).catch(function(e){
if(e.message==="auth"){cliBusy(false);status.remove();return}
+ // No such endpoint: this firmware predates the console (or was built
+ // without it). Say so instead of retrying a route that will never exist.
+ if(e.status===404){cliEnd(status,"This firmware has no console endpoint — nothing was sent.");return}
if(e.status===409&&e.reqid!==reqid){cliEnd(status,"Another sequence is still running — retry shortly.");return}
if(e.status===400||e.status===413){cliEnd(status,e.message||"Rejected by the node.");return}
// Ambiguous: the request may have landed even though the reply was lost.