nextcloud/server · error · Sabre\DAV\Exception\NotAuthenticated

Cannot authenticate over ajax calls

Error message

Cannot authenticate over ajax calls

What it means

Raised at the end of Sabre\Auth::check(): parent::check() (Sabre's standard Basic auth) rejected the credentials AND the request carries X-Requested-With containing XMLHttpRequest. Like error 315, the server answers 401 with a DummyBasic WWW-Authenticate realm to stop the browser from showing its native basic-auth popup, then throws NotAuthenticated. Root cause is genuinely invalid credentials; the ajax branch only changes the response shape.

Source

Thrown at apps/dav/lib/Connector/Sabre/Auth.php:211

				|| \OC_User::handleApacheAuth()
			) {
				$user = $this->userSession->getUser()->getUID();
				$this->currentUser = $user;
				$this->session->close();
				return [true, $this->principalPrefix . $user];
			}
		}

		$data = parent::check($request, $response);
		if ($data[0] === true) {
			$startPos = strrpos($data[1], '/') + 1;
			$user = $this->userSession->getUser()->getUID();
			$data[1] = substr_replace($data[1], $user, $startPos);
		} elseif (in_array('XMLHttpRequest', explode(',', $request->getHeader('X-Requested-With') ?? ''))) {
			// For ajax requests use dummy auth name to prevent browser popup in case of invalid creditials
			$response->addHeader('WWW-Authenticate', 'DummyBasic realm="' . $this->realm . '"');
			$response->setStatus(Http::STATUS_UNAUTHORIZED);
			throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls');
		}
		return $data;
	}
}

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Verify and refresh the credentials (create a new app password: Settings > Security, or `occ user:add-app-password <uid>`), then retry
  2. Confirm the username format (login name, not display name/email depending on instance settings)
  3. If your client handles its own auth UI, remove the X-Requested-With header for DAV calls so you receive the standard Basic challenge and can distinguish error sources
  4. Check for bruteforce throttling on repeated failures (`occ security:bruteforce:reset` for the affected IP)
Defensive patterns

Strategy: try-catch

Validate before calling

// JS: verify credentials once before entering DAV-heavy flows
const probe = await fetch('/remote.php/dav/avatars/current-user.png', {
  headers: { Authorization: 'Basic ' + btoa(user + ':' + pass) },
});
if (probe.status === 401) {
  showCredentialError(); // avoid triggering repeated DummyBasic 401s from DAV calls
}

Try / catch

try {
    await davRequest('PUT', url, body, basicAuthHeader);
} catch (e) {
    if (e.status === 401 && e.headers['WWW-Authenticate']?.startsWith('DummyBasic')) {
        // credentials rejected on an AJAX request — the popup was suppressed on purpose.
        // Stop retrying, surface a re-login UI, and refresh the stored app password.
        invalidateStoredCredentials();
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: An AJAX request to any Sabre/DAV endpoint with a wrong password, revoked app password, or wrong username, plus the X-Requested-With header. Non-AJAX callers get the ordinary Basic 401 challenge instead.

Common situations: App password revoked or regenerated (device list cleanup) while JS still uses the old one; username typos in embedded uploaders; password changed but cached in the client; custom apps sending X-Requested-With on DAV fetches by default (some frameworks add it globally).

Understand the failure class

Related errors


AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17). Data as JSON: /api/errors/35ecf1cc38f6eb5b. Report an issue: GitHub.