louislam/uptime-kuma · error · Error
SMSEagle API returned error: ${resp.data}
Error message
SMSEagle API returned error: ${resp.data} What it means
Thrown by the SMSEagle provider when the legacy HTTP API (apiv1) replies with a body that does not contain the literal substring 'OK'. The v1 SMS Eagle gateway returns a free-form text body where 'OK' (optionally followed by an ID) marks success; anything else is treated as an error and echoed verbatim into the message. Because the check uses String.indexOf on resp.data, it also implicitly assumes resp.data is a string.
Source
Thrown at server/notification-providers/smseagle.js:72
url.searchParams.append(recipientType, notification.smseagleRecipient);
if (!notification.smseagleRecipientType || notification.smseagleRecipientType === "smseagle-sms") {
url.searchParams.append("unicode", notification.smseagleEncoding ? "1" : "0");
url.searchParams.append("highpriority", notification.smseaglePriority ?? "0");
} else {
url.searchParams.append("duration", duration);
}
if (notification.smseagleRecipientType !== "smseagle-ring") {
url.searchParams.append("message", msg);
}
if (voiceId) {
url.searchParams.append("voice_id", voiceId);
}
let resp = await axios.get(url.toString(), config);
if (resp.data.indexOf("OK") === -1) {
let error = `SMSEagle API returned error: ${resp.data}`;
throw new Error(error);
}
return okMsg;
} else if (notification.smseagleApiType === "smseagle-apiv2") {
// according to https://www.smseagle.eu/docs/apiv2/
let config = {
headers: {
"access-token": notification.smseagleToken,
"Content-Type": "application/json",
},
};
config = this.getAxiosConfigWithProxy(config);
let encoding = notification.smseagleEncoding ? "unicode" : "standard";
let priority = notification.smseaglePriority ?? 0;
let postData = {
text: msg,View on GitHub (pinned to 6b5ea01557)
Solutions
- Open the SMSEagle web UI and verify the access_token under Setup > User Access Tokens is still active and matches notification.smseagleToken exactly.
- Confirm notification.smseagleUrl is the device base URL (no /api/v2 suffix) since v1 uses /http_api.
- Verify the recipient value matches an existing contact/group/phone in the device address book.
- Test the same URL+token with curl from the Uptime Kuma host to see the raw body returned and rule out network/proxy interference.
- If the device firmware now returns JSON, switch the provider config to smseagle-apiv2 (smseagle.js:76) which parses structured responses.
Example fix
// before (v1, fragile string check on possibly non-string body)
let resp = await axios.get(url.toString(), config);
if (resp.data.indexOf("OK") === -1) {
throw new Error(`SMSEagle API returned error: ${resp.data}`);
}
// after (guard the body type so a JSON/HTML body surfaces a clear message)
const body = typeof resp.data === "string" ? resp.data : JSON.stringify(resp.data);
if (!body.includes("OK")) {
throw new Error(`SMSEagle API returned error: ${body}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate token + url shape before the request
if (!notification.smseagleToken || !notification.smseagleUrl) {
throw new Error("SMSEagle token and URL are required");
}
try { new URL(notification.smseagleUrl); } catch { throw new Error("smseagleUrl is not a valid URL"); } Type guard
/** True when the v1 response body is a success string. */
function isSmseagleV1Ok(data) {
return typeof data === "string" && data.includes("OK");
} Try / catch
let resp;
try {
resp = await axios.get(url.toString(), config);
} catch (err) {
throw new Error(`SMSEagle request failed: ${err.message}`);
}
const body = typeof resp.data === "string" ? resp.data : JSON.stringify(resp.data);
if (!body.includes("OK")) {
throw new Error(`SMSEagle API returned error: ${body}`);
} Prevention
- Store the SMSEagle token in a secret manager and paste it without trailing whitespace.
- Keep smseagleUrl as the bare device URL; pick apiv2 if the firmware emits JSON.
- Add a smoke-test button in staging before enabling on production monitors.
When it happens
Trigger: A GET to {smseagleUrl}/http_api/{sendMethod} with an invalid/expired access_token, an unknown recipient (contact/group/phone), an unreachable modem, or a misconfigured base URL that returns an HTML error page. Also triggered if the device firmware returns JSON instead of plain text, since indexOf('OK') on a non-string would throw before this line.
Common situations: Token copied with trailing whitespace, SMSEagle hostname/IP changed after migration, device in offline/airplane mode, recipient contact name typo, or pointing smseagleUrl at the v2 JSON endpoint by mistake.
Related errors
- SMSEagle API returned an empty response
- SMSEagle API returned error: ${JSON.stringify(resp.data)}
- yzj's server did not respond with the expected result
- Headers must be valid JSON: ${e.message}
- Accepted status codes must be valid JSON: ${e.message}
AI-assisted analysis of louislam/uptime-kuma@6b5ea01557 (2026-08-12).
Data as JSON: /api/errors/6c062f40b73e8dd8.
Report an issue: GitHub.