gethomepage/homepage · error · Error

Failed parsing '${action}' response

Error message

Failed parsing '${action}' response

What it means

Thrown when httpProxy returned 200 for a Fritz!Box SOAP action but the response body could not be parsed: either JSON.parse(xml2json(data)) threw, or the deeply-nested element path (elements[0].elements[0].elements[0].elements) was not the expected shape. The router answered, but not in the SOAP envelope form the parser assumes.

Source

Thrown at src/widgets/fritzbox/proxy.js:42

      "</s:Body>" +
      "</s:Envelope>",
  };
  const apiUrl = `${apiBaseUrl}/igdupnp/control/${servicePath}`;
  const [status, , data] = await httpProxy(apiUrl, params);
  if (status !== 200) {
    logger.debug(`HTTP ${status} performing SoapRequest for ${service}->${action}`, data);
    throw new Error(`Failed fetching '${action}'`);
  }
  const response = {};
  try {
    const jsonData = JSON.parse(xml2json(data));
    const responseElements = jsonData?.elements?.[0]?.elements?.[0]?.elements?.[0]?.elements || [];
    responseElements.forEach((element) => {
      response[element.name] = element.elements?.[0].text || "";
    });
  } catch (e) {
    logger.debug(`Failed parsing ${service}->${action} response:`, data);
    throw new Error(`Failed parsing '${action}' response`);
  }

  return response;
}

export default async function fritzboxProxyHandler(req, res) {
  const { group, service, index } = req.query;
  const serviceWidget = await getServiceWidget(group, service, index);

  if (!serviceWidget) {
    res.status(500).json({ error: { message: "Service widget not found" } });
    return;
  }

  if (!serviceWidget.url) {
    res.status(500).json({ error: { message: "Service widget url not configured" } });
    return;
  }

View on GitHub (pinned to b6dca1ae03)

Solutions

  1. Inspect the debug log line 'Failed parsing ${service}->${action} response:' which dumps the raw data just before the throw.
  2. Confirm the action/service actually permits this call for the current Fritz!Box user; an unauthorized SOAP call often returns a fault envelope rather than a real payload.
  3. If a firmware update changed the envelope, adjust the element path parsing or upgrade Homepage to a version that matches the firmware.
  4. Remove any reverse proxy in front of the Fritz!Box that may transform/truncate the SOAP body.
  5. Verify the request is hitting /igdupnp/control/... and not a redirect that returns HTML.

Example fix

// before
const jsonData = JSON.parse(xml2json(data));
// after
let jsonData;
try {
  jsonData = JSON.parse(xml2json(data));
} catch (e) {
  throw new Error(`Failed to convert Fritz!Box response to JSON for ${action}: ${e.message}; raw=${Buffer.from(data).toString().slice(0, 200)}`);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Detect a SOAP fault early so it is not misread as a parse failure.
function looksLikeSoapFault(raw) {
  const s = Buffer.from(raw).toString();
  return /<fault>|<faultcode>|UPnPError/i.test(s);
}

Type guard

function isFritzboxSoapResponse(json) {
  const node = json?.elements?.[0]?.elements?.[0]?.elements?.[0]?.elements;
  return Array.isArray(node) && node.length > 0;
}

Try / catch

try {
  const jsonData = JSON.parse(xml2json(data));
  if (!isFritzboxSoapResponse(jsonData)) {
    throw new Error(`Unexpected Fritz!Box SOAP envelope shape for ${action}`);
  }
  // ...build response
} catch (e) {
  if (looksLikeSoapFault(data)) {
    res.status(403).json({ error: `Fritz!Box denied ${action} (SOAP fault)` });
  } else {
    res.status(502).json({ error: `Could not parse Fritz!Box ${action} response` });
  }
}

Prevention

When it happens

Trigger: The router returned a SOAP fault envelope (still 200) whose structure differs from a normal response, an HTML login/error page with a 200 status, a truncated response from a flaky proxy, or a firmware change that alters the envelope nesting.

Common situations: Fritz!Box returning a fault/permission-denied page with HTTP 200; an intermediary reverse proxy rewriting the body; partial response due to connection drop; a service whose response legitimately omits one nesting level (so the path resolves to undefined).

Related errors


AI-assisted analysis of gethomepage/homepage@b6dca1ae03 (2026-08-13). Data as JSON: /api/errors/db0dca1e5303eec0. Report an issue: GitHub.