appwrite/appwrite · error · Appwrite\Extend\Exception

user_jwt_and_cookie_set

user_jwt_and_cookie_set

Error message

JWT and cookie used in the same request. Use either `setJWT` or `setCookie`. Learn about which authentication method to use in the SSR docs: https://appwrite.io/docs/products/auth/server-side-rendering

What it means

The HTTP-request counterpart of error 301. Thrown in app/init/resources/request.php:526 when an inbound HTTP request resolves a non-empty user from the session cookie/store AND also carries an x-appwrite-jwt header. Appwrite disallows mixing session and JWT credentials in one HTTP request; the SSR docs are referenced for choosing one method.

Source

Thrown at app/init/resources/request.php:526

                        /** @var User $user */
                        $user = $dbForProject->getDocument('users', $store->getProperty('id', ''));
                    }
                }
            }
        }

        if (
            ! $user ||
            $user->isEmpty() // Check a document has been found in the DB
            || ! $user->sessionVerify($store->getProperty('secret', ''), $proofForToken)
        ) { // Validate user has valid login token
            $user = new User([]);
        }

        $authJWT = $request->getHeaderLine('x-appwrite-jwt', '');
        if (! empty($authJWT) && ! $project->isEmpty()) { // JWT authentication
            if (! $user->isEmpty()) {
                throw new Exception(Exception::USER_JWT_AND_COOKIE_SET);
            }

            $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
            try {
                $payload = $jwt->decode($authJWT);
            } catch (JWTException $error) {
                throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage());
            }

            $jwtUserId = $payload['userId'] ?? '';
            if (! empty($jwtUserId)) {
                if ($mode === APP_MODE_ADMIN) {
                    /** @var User $user */
                    $user = $dbForPlatform->getDocument('users', $jwtUserId);
                } else {
                    /** @var User $user */
                    $user = $dbForProject->getDocument('users', $jwtUserId);
                }

View on GitHub (pinned to cd368e707d)

Solutions

  1. For each HTTP call use EITHER setSession (cookie) OR setJWT, never both.
  2. Clear the session cookie before making JWT-authenticated server-side calls.
  3. In SSR, segregate client-side (cookie) and server-side (JWT) SDK instances so headers never combine.
  4. Inspect outbound request headers to confirm a single credential type.

Example fix

// before — SSR fetch sends both
client.setSession(sessionCookie);
client.setJWT(jwt);
// after — server-side call uses JWT only
client.setJWT(jwt);
Defensive patterns

Strategy: validation

Validate before calling

// Before each SSR HTTP call, ensure session and JWT are mutually exclusive
function assertSingleAuth(sessionCookie: string | null, jwt: string | null) {
  if (sessionCookie && jwt) {
    throw new Error('Do not send session and JWT together; choose one.');
  }
}

Try / catch

// Recover by dropping the cookie and retrying with JWT
try {
  await databases.listDocuments();
} catch (e) {
  if (e.code === 'user_jwt_and_cookie_set') {
    client.headers['Cookie'] = '';
    await databases.listDocuments();
  } else throw e;
}

Prevention

When it happens

Trigger: An HTTP API call (any /v1 endpoint) that has a valid session resolving to a user (via sessionVerify on the store secret/proof token) AND also sets x-appwrite-jwt. The !$user->isEmpty() guard trips before JWT decode.

Common situations: SSR framework holding a session cookie while the server-side fetch also attaches a JWT; SDK configured with both setSession and setJWT; cookie not cleared on logout before issuing a JWT-authenticated call; reverse proxy injecting cookies.

Understand the failure class

Related errors


AI-assisted analysis of appwrite/appwrite@cd368e707d (2026-08-12). Data as JSON: /api/errors/c9167405d3ba5b42. Report an issue: GitHub.