Mintplex-Labs/anything-llm · error
${result.error}
Error message
${result.error} What it means
The 400 reply from POST /admin/agent-skills/outlook/auth-url when outlookLib.getAuthUrl(redirectUri) reports failure. getAuthUrl re-reads the saved Outlook config and fails when clientId is empty (config incomplete) or validateOrganizationAuth() rejects it (organization authType without tenantId). Because the endpoint just wrote clientId into the config, seeing this usually means the saved value ended up empty (e.g. clientId was whitespace that trimmed to '') or the persisted authType/tenantId combination is inconsistent.
Source
Thrown at server/endpoints/utils/outlookAgentUtils.js:77
// If auth type changed, clear tokens as they won't work with different authority
if (
existingConfig.authType &&
existingConfig.authType !== validAuthType
) {
delete configUpdate.accessToken;
delete configUpdate.refreshToken;
delete configUpdate.tokenExpiry;
}
await outlookLib.OutlookBridge.updateConfig(configUpdate);
outlookLib.reset();
const redirectUri = getOutlookRedirectUri(request);
const result = await outlookLib.getAuthUrl(redirectUri);
if (!result.success) {
return response
.status(400)
.json({ success: false, error: result.error });
}
return response.status(200).json({ success: true, url: result.url });
} catch (e) {
console.error("Outlook auth URL error:", e);
response.status(500).json({ success: false, error: e.message });
}
}
);
app.get(
"/agent-skills/outlook/auth-callback",
[validatedRequest, isSingleUserMode],
async (request, response) => {
try {
const { code, error, error_description } = request.query;
View on GitHub (pinned to 3aec848f28)
Solutions
- Re-send clientId as a clean, non-empty string (no surrounding quotes) and include tenantId when using organization auth, then retry.
- GET the Outlook status endpoint to confirm the persisted config (clientId present, authType/tenantId consistent) before requesting the auth URL.
- If the stored config is scrambled, revoke (POST /admin/agent-skills/outlook/revoke) and re-configure from scratch.
Defensive patterns
Strategy: validation
Validate before calling
// Confirm persisted config is complete before requesting the auth URL
const status = await (await fetch("/api/agent-skills/outlook/status")).json();
if (!status.hasCredentials) throw new Error("Save clientId/clientSecret first");
if (status.config?.authType === "organizations" && !status.config?.tenantId) throw new Error("tenantId required for organization auth");
const { url } = await (await fetch("/api/admin/agent-skills/outlook/auth-url", { method: "POST", headers, body })).json(); Type guard
const outlookConfigComplete = (cfg) => !!cfg?.clientId?.trim() && (cfg.authType !== "organizations" || !!cfg.tenantId?.trim());
Prevention
- Trim credentials client-side so whitespace-only values never reach the server.
- Check the status endpoint after saving config and before generating the OAuth URL.
- If config state gets inconsistent, revoke and re-configure rather than patching around it.
When it happens
Trigger: clientId passed as whitespace/quotes trimming to an empty string; the config update persisted authType 'organization' with an empty tenantId; a stale or concurrently overwritten outlook_agent_config row between updateConfig and getConfig; redirect URI not registered in the Azure app (fails later in the browser flow rather than here).
Common situations: Quoted secret/id copied from a password manager; two admins saving conflicting Outlook configs; config row left half-written after a previous failed save; organization tenant toggled in the UI without re-entering the tenant.
Related errors
- Client ID and Client Secret are required.
- Tenant ID is required for organization-only authentication.
- ${e.message}
- Type "${type}" is not a valid type to sync.
- QEMU directory not found: ${dir}\nContents of ${parent}: ${c
AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18).
Data as JSON: /api/errors/9eaef81b8d2412d6.
Report an issue: GitHub.