diff --git a/packages/desktop-client/src/components/ServerContext.js b/packages/desktop-client/src/components/ServerContext.js
index 27050331855fd6ddaf2a89e4c0560fb731a94655..ae0fb1b4355779c4ce265f2c1a4dade38e57ad57 100644
--- a/packages/desktop-client/src/components/ServerContext.js
+++ b/packages/desktop-client/src/components/ServerContext.js
@@ -14,14 +14,6 @@ export const useServerURL = () => useContext(ServerContext).url;
 export const useServerVersion = () => useContext(ServerContext).version;
 export const useSetServerURL = () => useContext(ServerContext).setURL;
 
-async function getServerUrl() {
-  let url = (await send('get-server-url')) || '';
-  if (url === 'https://not-configured/') {
-    url = '';
-  }
-  return url;
-}
-
 async function getServerVersion() {
   let { error, version } = await send('get-server-version');
   if (error) {
@@ -36,7 +28,7 @@ export function ServerProvider({ children }) {
 
   useEffect(() => {
     async function run() {
-      setServerURL(await getServerUrl());
+      setServerURL(await send('get-server-url'));
       setVersion(await getServerVersion());
     }
     run();
@@ -46,7 +38,7 @@ export function ServerProvider({ children }) {
     async (url, opts = {}) => {
       let { error } = await send('set-server-url', { ...opts, url });
       if (!error) {
-        setServerURL(await getServerUrl());
+        setServerURL(await send('get-server-url'));
         setVersion(await getServerVersion());
       }
       return { error };
diff --git a/packages/desktop-client/src/components/manager/subscribe/common.tsx b/packages/desktop-client/src/components/manager/subscribe/common.tsx
index ed96e65706752a7e13eb638faa145a07caba2346..9e0082fb7ae99124e36e618783f804d48f7cf010 100644
--- a/packages/desktop-client/src/components/manager/subscribe/common.tsx
+++ b/packages/desktop-client/src/components/manager/subscribe/common.tsx
@@ -41,7 +41,8 @@ export function useBootstrapped() {
       };
 
       let url = await send('get-server-url');
-      if (url == null) {
+      let bootstrapped = await send('get-did-bootstrap');
+      if (url == null && !bootstrapped) {
         // A server hasn't been specified yet
         let serverURL = window.location.origin;
         let result = await send('subscribe-needs-bootstrap', {
diff --git a/packages/desktop-client/src/hooks/useFeatureFlag.ts b/packages/desktop-client/src/hooks/useFeatureFlag.ts
index 0d55d746aaba59a09df748e53ff56f86dc98de2c..7aa96bfb45655c2f213e35691771e6bc0df3e81d 100644
--- a/packages/desktop-client/src/hooks/useFeatureFlag.ts
+++ b/packages/desktop-client/src/hooks/useFeatureFlag.ts
@@ -1,6 +1,7 @@
 import { useSelector } from 'react-redux';
 
 const DEFAULT_FEATURE_FLAG_STATE: Record<string, boolean> = {
+  reportBudget: false,
   syncAccount: false,
   goalTemplatesEnabled: false,
 };
diff --git a/packages/loot-core/src/server/accounts/sync.ts b/packages/loot-core/src/server/accounts/sync.ts
index 18b6aabc033acebffb804eff38ee1d95f84db2a0..7cf272d1f7c165e82c6c5f9c288755e91422acdc 100644
--- a/packages/loot-core/src/server/accounts/sync.ts
+++ b/packages/loot-core/src/server/accounts/sync.ts
@@ -77,27 +77,27 @@ export async function getAccounts(userId, userKey, id) {
 
 export async function getNordigenAccounts(userId, userKey, id) {
   const userToken = await asyncStorage.getItem('user-token');
-  if (userToken) {
-    let res = await post(
-      getServer().NORDIGEN_SERVER + '/accounts',
-      {
-        userId,
-        key: userKey,
-        item_id: id,
-      },
-      {
-        'X-ACTUAL-TOKEN': userToken,
-      },
-    );
+  if (!userToken) return;
 
-    let { accounts } = res;
+  let res = await post(
+    getServer().NORDIGEN_SERVER + '/accounts',
+    {
+      userId,
+      key: userKey,
+      item_id: id,
+    },
+    {
+      'X-ACTUAL-TOKEN': userToken,
+    },
+  );
 
-    accounts.forEach(acct => {
-      acct.balances.current = getAccountBalance(acct);
-    });
+  let { accounts } = res;
 
-    return accounts;
-  }
+  accounts.forEach(acct => {
+    acct.balances.current = getAccountBalance(acct);
+  });
+
+  return accounts;
 }
 
 export function fromPlaid(trans) {
@@ -182,41 +182,40 @@ async function downloadNordigenTransactions(
   since,
 ) {
   let userToken = await asyncStorage.getItem('user-token');
-  if (userToken) {
-    const endDate = new Date().toISOString().split('T')[0];
-
-    const res = await post(
-      getServer().NORDIGEN_SERVER + '/transactions',
-      {
-        userId: userId,
-        key: userKey,
-        requisitionId: bankId,
-        accountId: acctId,
-        startDate: since,
-        endDate,
-      },
-      {
-        'X-ACTUAL-TOKEN': userToken,
-      },
-    );
+  if (!userToken) return;
 
-    if (res.error_code) {
-      throw BankSyncError(res.error_type, res.error_code);
-    }
+  const endDate = new Date().toISOString().split('T')[0];
 
-    const {
-      transactions: { all },
-      balances,
-      startingBalance,
-    } = res;
+  const res = await post(
+    getServer().NORDIGEN_SERVER + '/transactions',
+    {
+      userId: userId,
+      key: userKey,
+      requisitionId: bankId,
+      accountId: acctId,
+      startDate: since,
+      endDate,
+    },
+    {
+      'X-ACTUAL-TOKEN': userToken,
+    },
+  );
 
-    return {
-      transactions: all,
-      accountBalance: balances,
-      startingBalance,
-    };
+  if (res.error_code) {
+    throw BankSyncError(res.error_type, res.error_code);
   }
-  return;
+
+  const {
+    transactions: { all },
+    balances,
+    startingBalance,
+  } = res;
+
+  return {
+    transactions: all,
+    accountBalance: balances,
+    startingBalance,
+  };
 }
 
 async function resolvePayee(trans, payeeName, payeesToCreate) {
diff --git a/packages/loot-core/src/server/cloud-storage.ts b/packages/loot-core/src/server/cloud-storage.ts
index 12dafc4bddfcd57451fc12d05067435693a1df17..6c7cc80b5fca7f835cbf20152cf8d62e3a3275be 100644
--- a/packages/loot-core/src/server/cloud-storage.ts
+++ b/packages/loot-core/src/server/cloud-storage.ts
@@ -326,7 +326,7 @@ export async function possiblyUpload() {
 }
 
 export async function removeFile(fileId) {
-  const [[, userToken]] = await asyncStorage.multiGet(['user-token']);
+  let userToken = await asyncStorage.getItem('user-token');
 
   await post(getServer().SYNC_SERVER + '/delete-user-file', {
     token: userToken,
diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts
index 95c10bd46313cccb012e272be65418cf34524472..f6a1e998c2f79409aa09017e0705b1d9df5bd882 100644
--- a/packages/loot-core/src/server/main.ts
+++ b/packages/loot-core/src/server/main.ts
@@ -81,7 +81,6 @@ import { uniqueFileName, idFromFileName } from './util/budget-name';
 
 let DEMO_BUDGET_ID = '_demo-budget';
 let TEST_BUDGET_ID = '_test-budget';
-let UNCONFIGURED_SERVER = 'https://not-configured/';
 
 // util
 
@@ -1132,40 +1131,42 @@ handlers['accounts-sync'] = async function ({ id }) {
 handlers['secret-set'] = async function ({ name, value }) {
   let userToken = await asyncStorage.getItem('user-token');
 
-  if (userToken) {
-    try {
-      return await post(
-        getServer().BASE_SERVER + '/secret',
-        {
-          name,
-          value,
-        },
-        {
-          'X-ACTUAL-TOKEN': userToken,
-        },
-      );
-    } catch (error) {
-      console.error(error);
-      return { error: 'failed' };
-    }
+  if (!userToken) {
+    return { error: 'unauthorized' };
+  }
+
+  try {
+    return await post(
+      getServer().BASE_SERVER + '/secret',
+      {
+        name,
+        value,
+      },
+      {
+        'X-ACTUAL-TOKEN': userToken,
+      },
+    );
+  } catch (error) {
+    console.error(error);
+    return { error: 'failed' };
   }
-  return { error: 'unauthorized' };
 };
 
 handlers['secret-check'] = async function (name) {
   let userToken = await asyncStorage.getItem('user-token');
 
-  if (userToken) {
-    try {
-      return await get(getServer().BASE_SERVER + '/secret/' + name, {
-        'X-ACTUAL-TOKEN': userToken,
-      });
-    } catch (error) {
-      console.error(error);
-      return { error: 'failed' };
-    }
+  if (!userToken) {
+    return { error: 'unauthorized' };
+  }
+
+  try {
+    return await get(getServer().BASE_SERVER + '/secret/' + name, {
+      'X-ACTUAL-TOKEN': userToken,
+    });
+  } catch (error) {
+    console.error(error);
+    return { error: 'failed' };
   }
-  return { error: 'unauthorized' };
 };
 
 handlers['nordigen-poll-web-token'] = async function ({
@@ -1173,62 +1174,59 @@ handlers['nordigen-poll-web-token'] = async function ({
   requisitionId,
 }) {
   let userToken = await asyncStorage.getItem('user-token');
+  if (!userToken) return null;
 
-  if (userToken) {
-    let startTime = Date.now();
-    stopPolling = false;
+  let startTime = Date.now();
+  stopPolling = false;
 
-    async function getData(cb) {
-      if (stopPolling) {
-        return;
-      }
+  async function getData(cb) {
+    if (stopPolling) {
+      return;
+    }
 
-      if (Date.now() - startTime >= 1000 * 60 * 10) {
-        cb('timeout');
-        return;
-      }
+    if (Date.now() - startTime >= 1000 * 60 * 10) {
+      cb('timeout');
+      return;
+    }
 
-      let data = await post(
-        getServer().NORDIGEN_SERVER + '/get-accounts',
-        {
-          upgradingAccountId,
-          requisitionId,
-        },
-        {
-          'X-ACTUAL-TOKEN': userToken,
-        },
-      );
+    let data = await post(
+      getServer().NORDIGEN_SERVER + '/get-accounts',
+      {
+        upgradingAccountId,
+        requisitionId,
+      },
+      {
+        'X-ACTUAL-TOKEN': userToken,
+      },
+    );
 
-      if (data) {
-        if (data.error) {
-          cb('unknown');
-        } else {
-          cb(null, data);
-        }
+    if (data) {
+      if (data.error) {
+        cb('unknown');
       } else {
-        setTimeout(() => getData(cb), 3000);
+        cb(null, data);
       }
+    } else {
+      setTimeout(() => getData(cb), 3000);
     }
-
-    return new Promise(resolve => {
-      getData((error, data) => {
-        if (error) {
-          resolve({ error });
-        } else {
-          resolve({ data });
-        }
-      });
-    });
   }
 
-  return null;
+  return new Promise(resolve => {
+    getData((error, data) => {
+      if (error) {
+        resolve({ error });
+      } else {
+        resolve({ data });
+      }
+    });
+  });
 };
 
 handlers['nordigen-status'] = async function () {
   const userToken = await asyncStorage.getItem('user-token');
 
   if (!userToken) {
-    return Promise.reject({ error: 'unauthorized' });
+    return { error: 'unauthorized' };
   }
 
   return post(
@@ -1244,7 +1242,7 @@ handlers['nordigen-get-banks'] = async function (country) {
   const userToken = await asyncStorage.getItem('user-token');
 
   if (!userToken) {
-    return Promise.reject({ error: 'unauthorized' });
+    return { error: 'unauthorized' };
   }
 
   return post(
@@ -1268,25 +1266,26 @@ handlers['nordigen-create-web-token'] = async function ({
 }) {
   let userToken = await asyncStorage.getItem('user-token');
 
-  if (userToken) {
-    try {
-      return await post(
-        getServer().NORDIGEN_SERVER + '/create-web-token',
-        {
-          upgradingAccountId,
-          institutionId,
-          accessValidForDays,
-        },
-        {
-          'X-ACTUAL-TOKEN': userToken,
-        },
-      );
-    } catch (error) {
-      console.error(error);
-      return { error: 'failed' };
-    }
+  if (!userToken) {
+    return { error: 'unauthorized' };
+  }
+
+  try {
+    return await post(
+      getServer().NORDIGEN_SERVER + '/create-web-token',
+      {
+        upgradingAccountId,
+        institutionId,
+        accessValidForDays,
+      },
+      {
+        'X-ACTUAL-TOKEN': userToken,
+      },
+    );
+  } catch (error) {
+    console.error(error);
+    return { error: 'failed' };
   }
-  return { error: 'unauthorized' };
 };
 
 handlers['nordigen-accounts-sync'] = async function ({ id }) {
@@ -1418,8 +1417,11 @@ handlers['account-unlink'] = mutator(async function ({ id }) {
   // No more accounts are associated with this bank. We can remove
   // it from Nordigen.
   let userToken = await asyncStorage.getItem('user-token');
+  if (!userToken) {
+    return 'ok';
+  }
 
-  if (userToken && count === 0) {
+  if (count === 0) {
     let { bank_id: requisitionId } = await db.first(
       'SELECT bank_id FROM banks WHERE id = ?',
       [bankId],
@@ -1624,10 +1626,14 @@ handlers['key-test'] = async function ({ fileId, password }) {
   return {};
 };
 
+handlers['get-did-bootstrap'] = async function () {
+  return Boolean(await asyncStorage.getItem('did-bootstrap'));
+};
+
 handlers['subscribe-needs-bootstrap'] = async function ({
   url,
 }: { url? } = {}) {
-  if (getServer(url).BASE_SERVER === UNCONFIGURED_SERVER) {
+  if (!getServer(url)) {
     return { bootstrapped: true, hasServer: false };
   }
 
@@ -1666,45 +1672,48 @@ handlers['subscribe-bootstrap'] = async function ({ password }) {
   return { error: 'internal' };
 };
 
-handlers['subscribe-set-user'] = async function ({ token }) {
-  await asyncStorage.setItem('user-token', token);
-};
-
 handlers['subscribe-get-user'] = async function () {
-  if (getServer() && getServer().BASE_SERVER === UNCONFIGURED_SERVER) {
+  if (!getServer()) {
+    if (!(await asyncStorage.getItem('did-bootstrap'))) {
+      return null;
+    }
     return { offline: false };
   }
 
   let userToken = await asyncStorage.getItem('user-token');
 
-  if (userToken) {
-    try {
-      const res = await get(getServer().SIGNUP_SERVER + '/validate', {
-        headers: {
-          'X-ACTUAL-TOKEN': userToken,
-        },
-      });
-      const { status, reason } = JSON.parse(res);
+  if (!userToken) {
+    return null;
+  }
 
-      if (status === 'error') {
-        if (reason === 'unauthorized') {
-          return null;
-        }
-        return { offline: true };
-      }
+  try {
+    const res = await get(getServer().SIGNUP_SERVER + '/validate', {
+      headers: {
+        'X-ACTUAL-TOKEN': userToken,
+      },
+    });
+    const { status, reason } = JSON.parse(res);
 
-      return { offline: false };
-    } catch (e) {
-      console.log(e);
+    if (status === 'error') {
+      if (reason === 'unauthorized') {
+        return null;
+      }
       return { offline: true };
     }
-  }
 
-  return null;
+    return { offline: false };
+  } catch (e) {
+    console.log(e);
+    return { offline: true };
+  }
 };
 
 handlers['subscribe-change-password'] = async function ({ password }) {
   let userToken = await asyncStorage.getItem('user-token');
+  if (!userToken) {
+    return { error: 'not-logged-in' };
+  }
+
   try {
     await post(getServer().SIGNUP_SERVER + '/change-password', {
       token: userToken,
@@ -1742,7 +1751,7 @@ handlers['subscribe-sign-out'] = async function () {
 };
 
 handlers['get-server-version'] = async function () {
-  if (!getServer() || getServer().BASE_SERVER === UNCONFIGURED_SERVER) {
+  if (!getServer()) {
     return { error: 'no-server' };
   }
 
@@ -1764,7 +1773,9 @@ handlers['get-server-url'] = async function () {
 };
 
 handlers['set-server-url'] = async function ({ url, validate = true }) {
-  if (url != null) {
+  if (url == null) {
+    await asyncStorage.removeItem('user-token');
+  } else {
     if (validate) {
       // Validate the server is running
       let { error } = await runHandler(handlers['subscribe-needs-bootstrap'], {
@@ -1774,12 +1785,10 @@ handlers['set-server-url'] = async function ({ url, validate = true }) {
         return { error };
       }
     }
-  } else {
-    // When the server isn't configured, we just use a placeholder
-    url = UNCONFIGURED_SERVER;
   }
 
-  asyncStorage.setItem('server-url', url);
+  await asyncStorage.setItem('server-url', url);
+  await asyncStorage.setItem('did-bootstrap', true);
   setServer(url);
   return {};
 };
@@ -2170,7 +2179,7 @@ async function loadBudget(id) {
   // Older versions didn't tag the file with the current user, so do
   // so now
   if (!prefs.getPrefs().userId) {
-    let [[, userId]] = await asyncStorage.multiGet(['user-token']);
+    let userId = await asyncStorage.getItem('user-token');
     prefs.savePrefs({ userId });
   }
 
@@ -2244,10 +2253,12 @@ async function loadBudget(id) {
   if (process.env.NODE_ENV !== 'test') {
     if (process.env.IS_BETA || id === DEMO_BUDGET_ID) {
       setSyncingMode('disabled');
-    } else if (id === TEST_BUDGET_ID) {
-      await asyncStorage.setItem('lastBudget', id);
     } else {
-      setSyncingMode('enabled');
+      if (getServer()) {
+        setSyncingMode('enabled');
+      } else {
+        setSyncingMode('disabled');
+      }
 
       await asyncStorage.setItem('lastBudget', id);
 
@@ -2405,10 +2416,19 @@ export async function initApp(isDev, socketName) {
   // }
   // }
 
-  const url = await asyncStorage.getItem('server-url');
-  if (url) {
-    setServer(url);
+  let url = await asyncStorage.getItem('server-url');
+
+  // TODO: remove this if statement after a few releases
+  if (url === 'https://not-configured/') {
+    url = null;
+    await asyncStorage.setItem('server-url', null);
+    await asyncStorage.setItem('did-bootstrap', true);
+  }
+
+  if (!url) {
+    await asyncStorage.removeItem('user-token');
   }
+  setServer(url);
 
   connection.init(socketName, app.handlers);
 
diff --git a/packages/loot-core/src/types/main-handlers.d.ts b/packages/loot-core/src/types/main-handlers.d.ts
index f7158fb2a177181d3c30ea34c20f25fa8e1b512d..669d1acbc96ccf776bfd8d78b9bc89bc6389d526 100644
--- a/packages/loot-core/src/types/main-handlers.d.ts
+++ b/packages/loot-core/src/types/main-handlers.d.ts
@@ -248,6 +248,8 @@ export interface MainHandlers {
 
   'key-test': (arg: { fileId; password }) => Promise<unknown>;
 
+  'get-did-bootstrap': () => Promise<boolean>;
+
   'subscribe-needs-bootstrap': (
     args: { url } = {},
   ) => Promise<
@@ -256,8 +258,6 @@ export interface MainHandlers {
 
   'subscribe-bootstrap': (arg: { password }) => Promise<{ error: string }>;
 
-  'subscribe-set-user': (arg: { token }) => Promise<unknown>;
-
   'subscribe-get-user': () => Promise<{ offline: boolean } | null>;
 
   'subscribe-change-password': (arg: {
diff --git a/upcoming-release-notes/984.md b/upcoming-release-notes/984.md
new file mode 100644
index 0000000000000000000000000000000000000000..525352b989f483f8ac4698d50a9922fe1bd2c2a3
--- /dev/null
+++ b/upcoming-release-notes/984.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [j-f1]
+---
+
+Stop frontend from attempting to connect to an invalid server when no server is configured