gethomepage/homepage · error · Error
Unable to login to Duplicati (status ${status})
Error message
Unable to login to Duplicati (status ${status}) What it means
Thrown by the Duplicati proxy's login step when the POST to the login endpoint returns any status other than 200. Duplicati requires a login that exchanges the configured password for an AccessToken before any data request, so a failed login aborts the whole proxy call chain.
Source
Thrown at src/widgets/duplicati/proxy.js:62
nextRun: nextRunTime?.toUTC().toISO() ?? null,
};
}
async function login(widget) {
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}`,
},
});View on GitHub (pinned to b6dca1ae03)
Solutions
- Re-enter the Duplicati password in the Homepage widget config exactly as set in Duplicati, trimming any whitespace.
- Confirm the Duplicati base URL and port are correct and reachable from the Homepage container.
- Log in to Duplicati's web UI with the same password to confirm the credential itself works.
- Check the reverse proxy passes the JSON POST body and Content-Type: application/json through unchanged.
- Raise Homepage debug logging to capture the status code that precedes the throw, then map it (401 vs 500 vs 502) to the right cause.
Example fix
// before
if (status !== 200) {
throw new Error(`Unable to login to Duplicati (status ${status})`);
}
// after
if (status !== 200) {
throw new Error(`Duplicati login at ${loginUrl} failed (HTTP ${status}): ${Buffer.from(data).toString().slice(0, 200)}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity-check the Duplicati config before invoking login.
function validateDuplicatiWidget(w) {
if (!w?.url || !/^https?:\/\//.test(w.url)) throw new Error("Duplicati url invalid");
if (typeof w.password !== "string" || w.password.length === 0) throw new Error("Duplicati password missing");
return true;
} Type guard
function hasDuplicatiCredentials(widget) {
return Boolean(widget && typeof widget.url === "string" && typeof widget.password === "string" && widget.password.length > 0);
} Try / catch
try {
const token = await login(widget);
return await apiGet(widget, endpoint, token);
} catch (err) {
if (/Unable to login/.test(err.message)) {
res.status(401).json({ error: "Duplicati login failed", hint: "check password/URL" });
} else {
throw err;
}
} Prevention
- Test the Duplicati password by logging into the web UI before configuring the widget.
- Keep the Duplicati URL and password in env-backed config so they survive container rebuilds.
- Log the failing status separately so you can distinguish 401 (credential) from 502 (proxy).
When it happens
Trigger: Wrong password configured for the Duplicati widget (login returns 401/422), Duplicati server unreachable or returning 500, login URL malformed so it 404s, or Duplicati restart/capacity lock returning 503 during the window the widget polls.
Common situations: Password copied with surrounding whitespace or quotes; Duplicati password changed but widget config not updated; base URL wrong (path/port/scheme); Duplicati version change that altered the login endpoint; running behind a reverse proxy that strips the POST body or sets the wrong Content-Type.
Related errors
- Duplicati request failed for ${endpoint}
- Error getting data from Audiobookshelf: ${status}. Data: ${d
- Duplicati login response did not include an access token
- TrueNAS authentication failed
- Authorization failure getting data from glances API. Data: $
AI-assisted analysis of gethomepage/homepage@b6dca1ae03 (2026-08-13).
Data as JSON: /api/errors/d36b3a45cc21d8e9.
Report an issue: GitHub.