Dolibarr/dolibarr · info

ERROR_NOT_LOGGED

ERROR_NOT_LOGGED

Error message

ERROR_NOT_LOGGED

What it means

When a request reaches a protected page without a logged-in user, Dolibarr normally redirects to the login page. However, if NOREDIRECTBYMAINTOLOGIN is defined (e.g. AJAX components like selectsearchbox.php), main.inc.php instead returns the literal string 'ERROR_NOT_LOGGED' so the including script can handle the unauthenticated state itself; a 401 is optionally sent when the user agent is 'securitytest'.

Solutions

  1. Log in again or re-establish the session before calling the AJAX endpoint.
  2. Make the AJAX client handle the ERROR_NOT_LOGGED return by redirecting to the login page.
  3. In custom scripts, check the return value of the main.inc.php inclusion and handle it.
  4. For tests, expect the 401 status rather than parsing HTML.
  5. If a full-page redirect is desired, remove the NOREDIRECTBYMAINTOLOGIN define.

Example fix

// before
@require './main.inc.php';
// after
$res = require './main.inc.php';
if ($res === 'ERROR_NOT_LOGGED') { top_httphead('application/json'); echo '{"error":"not_logged"}'; exit; }
Defensive patterns

Strategy: fallback

Validate before calling

if (empty($user->id)) { /* not logged in yet, handle before protected calls */ }

Try / catch

$res = require __DIR__.'/main.inc.php';
if ($res === 'ERROR_NOT_LOGGED') { http_response_code(401); exit; }

Prevention

When it happens

Trigger: Session expired or no user in session while including main.inc.php in a script that defines NOREDIRECTBYMAINTOLOGIN; AJAX endpoints hit after a session timeout.

Common situations: AJAX search components polling after the session timed out; headless/API-style scripts expecting a return code rather than a redirect; automated tests sending User-Agent 'securitytest' and receiving 401.

Related errors


AI-assisted analysis of Dolibarr/dolibarr@598aa4bdad (2026-09-14). Data as JSON: /api/errors/150427317a8bb455. Report an issue: GitHub.

Appendix: source

Thrown at htdocs/main.inc.php:865

				$reshook = $hookmanager->executeHooks('afterLoginFailed', $parameters, $user, $action); // Note that $action and $object may have been modified by some hooks
				if ($reshook < 0) {
					$error++;
				}

				// Note: exit is done in next chapter
			}
		}

		// End test login / passwords
		if (!$login || (in_array('ldap', $authmode) && !in_array('openid_connect', $authmode) && empty($passwordtotest))) {     // With LDAP we refused empty password because some LDAP are "opened" for anonymous access so connection is a success.
			// No data to test login, so we show the login page.
			dol_syslog("--- Access to ".(empty($_SERVER["REQUEST_METHOD"]) ? '' : $_SERVER["REQUEST_METHOD"].' ').$_SERVER["PHP_SELF"]." - action=".GETPOST('action', 'aZ09')." - actionlogin=".GETPOST('actionlogin', 'aZ09')." - showing the login form and exit", LOG_NOTICE);
			if (defined('NOREDIRECTBYMAINTOLOGIN')) {
				// When used with NOREDIRECTBYMAINTOLOGIN set, the http header must already be set when including the main.
				// See example with selectsearchbox.php. This case is reserved for the selectesearchbox.php so we can
				// report a message to ask to login when search ajax component is used after a timeout.
				//top_httphead();
				return 'ERROR_NOT_LOGGED';
			} else {
				if (!empty($_SERVER["HTTP_USER_AGENT"]) && $_SERVER["HTTP_USER_AGENT"] == 'securitytest') {
					http_response_code(401); // It makes easier to understand if session was broken during security tests
				}

				// Show login form
				dol_loginfunction($langs, $conf, (!empty($mysoc) ? $mysoc : ''));	// This include http headers
			}
			exit;
		}

		$resultFetchUser = $user->fetch(0, $login, '', 1, ($entitytotest > 0 ? $entitytotest : -1)); // value for $login was retrieved previously when checking password.

		if ($resultFetchUser <= 0 || $user->isNotIntoValidityDateRange()) {
			dol_syslog('User not found or not valid, connection refused');
			session_destroy();
			session_set_cookie_params(0, '/', null, !empty($dolibarr_main_force_https), true); // Add tag secure and httponly on session cookie
			session_name($sessionname);

View on GitHub (pinned to 598aa4bdad)