gethomepage/homepage · error · Error
Duplicati login response did not include an access token
Error message
Duplicati login response did not include an access token
What it means
Thrown after a successful (200) Duplicati login when the response body parses as JSON but does not contain an AccessToken field. The HTTP layer succeeded but the contract is broken: Duplicati's login endpoint is expected to return { AccessToken: "..." }, and without it the subsequent API calls have no bearer token.
Source
Thrown at src/widgets/duplicati/proxy.js:67
const loginUrl = new URL(formatApiCall(widgets[widget.type].api, { endpoint: "auth/login", ...widget }));
const [status, , data] = await httpProxy(loginUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
Password: String(widget.password),
RememberMe: true,
}),
});
if (status !== 200) {
throw new Error(`Unable to login to Duplicati (status ${status})`);
}
const body = asJson(data);
if (!body?.AccessToken) {
throw new Error("Duplicati login response did not include an access token");
}
return body.AccessToken;
}
async function apiGet(widget, endpoint, accessToken) {
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
const [status, , data] = await httpProxy(url, {
method: "GET",
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
if (status !== 200) {
throw new Error(`Duplicati request failed for ${endpoint}`);
}
View on GitHub (pinned to b6dca1ae03)
Solutions
- Verify the widget's URL targets the Duplicati API base, not the web UI root.
- Reproduce the login POST with curl from the Homepage host and inspect the response JSON to confirm AccessToken is present.
- Check the Duplicati version against the one Homepage's widget was written for; update Homepage or align versions.
- Ensure no reverse proxy rewrites the login path or serves a static page on it.
- Temporarily log asJson(data) to see exactly what Duplicati returned and adjust the parsing expectation.
Example fix
// before
if (!body?.AccessToken) {
throw new Error("Duplicati login response did not include an access token");
}
// after
if (!body?.AccessToken) {
throw new Error(`Duplicati login response missing AccessToken; got: ${JSON.stringify(body).slice(0, 200)}`);
} Defensive patterns
Strategy: type-guard
Type guard
function isDuplicatiLoginBody(v) {
return typeof v === "object" && v !== null && typeof v.AccessToken === "string" && v.AccessToken.length > 0;
} Try / catch
const body = asJson(data);
if (!isDuplicatiLoginBody(body)) {
// Surface what Duplicati actually returned so the misconfigured proxy is obvious.
throw new Error(`Unexpected Duplicati login body: ${JSON.stringify(body).slice(0, 200)}`);
} Prevention
- Point the widget at the Duplicati API base, not the UI root, so the login endpoint returns JSON, not HTML.
- Pin the Duplicati version in test fixtures so a schema change is caught before production.
- Assert the AccessToken shape in an integration test against a real Duplicati instance.
When it happens
Trigger: Duplicati returns 200 with an unexpected body shape (an HTML page from a reverse proxy, an empty object, an error envelope), an API version change renamed the field, or the login endpoint was redirected to a UI page that still returned 200.
Common situations: Reverse proxy (e.g. a custom 200 error/landing page) intercepting the login path; Duplicati upgrade that changed the auth response schema; base URL pointing at the web UI root instead of the API; a captive/redirecting network returning 200 HTML instead of the API JSON.
Related errors
- Unable to login to Duplicati (status ${status})
- Duplicati request failed for ${endpoint}
- Error getting data from Audiobookshelf: ${status}. Data: ${d
- TrueNAS authentication failed
- Missing Glances URL
AI-assisted analysis of gethomepage/homepage@b6dca1ae03 (2026-08-13).
Data as JSON: /api/errors/65782ecdeafa8fda.
Report an issue: GitHub.