{"record":{"id":"6c062f40b73e8dd8","repo":"louislam/uptime-kuma","slug":"smseagle-api-returned-error-resp-data","errorCode":null,"errorMessage":"SMSEagle API returned error: ${resp.data}","messagePattern":"SMSEagle API returned error: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/notification-providers/smseagle.js","lineNumber":72,"sourceCode":"                url.searchParams.append(recipientType, notification.smseagleRecipient);\n                if (!notification.smseagleRecipientType || notification.smseagleRecipientType === \"smseagle-sms\") {\n                    url.searchParams.append(\"unicode\", notification.smseagleEncoding ? \"1\" : \"0\");\n                    url.searchParams.append(\"highpriority\", notification.smseaglePriority ?? \"0\");\n                } else {\n                    url.searchParams.append(\"duration\", duration);\n                }\n                if (notification.smseagleRecipientType !== \"smseagle-ring\") {\n                    url.searchParams.append(\"message\", msg);\n                }\n                if (voiceId) {\n                    url.searchParams.append(\"voice_id\", voiceId);\n                }\n\n                let resp = await axios.get(url.toString(), config);\n\n                if (resp.data.indexOf(\"OK\") === -1) {\n                    let error = `SMSEagle API returned error: ${resp.data}`;\n                    throw new Error(error);\n                }\n\n                return okMsg;\n            } else if (notification.smseagleApiType === \"smseagle-apiv2\") {\n                // according to https://www.smseagle.eu/docs/apiv2/\n                let config = {\n                    headers: {\n                        \"access-token\": notification.smseagleToken,\n                        \"Content-Type\": \"application/json\",\n                    },\n                };\n                config = this.getAxiosConfigWithProxy(config);\n\n                let encoding = notification.smseagleEncoding ? \"unicode\" : \"standard\";\n                let priority = notification.smseaglePriority ?? 0;\n\n                let postData = {\n                    text: msg,","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/louislam/uptime-kuma/blob/6b5ea0155793e666666745fb8d6fef1e829543a2/server/notification-providers/smseagle.js#L54-L90","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (v1, fragile string check on possibly non-string body)\nlet resp = await axios.get(url.toString(), config);\nif (resp.data.indexOf(\"OK\") === -1) {\n    throw new Error(`SMSEagle API returned error: ${resp.data}`);\n}\n// after (guard the body type so a JSON/HTML body surfaces a clear message)\nconst body = typeof resp.data === \"string\" ? resp.data : JSON.stringify(resp.data);\nif (!body.includes(\"OK\")) {\n    throw new Error(`SMSEagle API returned error: ${body}`);\n}","handlingStrategy":"try-catch","validationCode":"// Validate token + url shape before the request\nif (!notification.smseagleToken || !notification.smseagleUrl) {\n    throw new Error(\"SMSEagle token and URL are required\");\n}\ntry { new URL(notification.smseagleUrl); } catch { throw new Error(\"smseagleUrl is not a valid URL\"); }","typeGuard":"/** True when the v1 response body is a success string. */\nfunction isSmseagleV1Ok(data) {\n    return typeof data === \"string\" && data.includes(\"OK\");\n}","tryCatchPattern":"let resp;\ntry {\n    resp = await axios.get(url.toString(), config);\n} catch (err) {\n    throw new Error(`SMSEagle request failed: ${err.message}`);\n}\nconst body = typeof resp.data === \"string\" ? resp.data : JSON.stringify(resp.data);\nif (!body.includes(\"OK\")) {\n    throw new Error(`SMSEagle API returned error: ${body}`);\n}","preventionTips":["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."],"tags":["smseagle","notification-provider","api-response","http","config"],"backgroundTag":null,"analyzedSha":"6b5ea0155793e666666745fb8d6fef1e829543a2","analyzedAt":"2026-08-12T23:42:12.959Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}