diff --git a/lib/actions/activity-actions.js b/lib/actions/activity-actions.js
--- a/lib/actions/activity-actions.js
+++ b/lib/actions/activity-actions.js
@@ -1,6 +1,9 @@
 // @flow
 
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import {
+  extractKeyserverIDFromID,
+  extractKeyserverIDFromIDOptional,
+} from '../keyserver-conn/keyserver-call-utils.js';
 import { useKeyserverCall } from '../keyserver-conn/keyserver-call.js';
 import type { CallKeyserverEndpoint } from '../keyserver-conn/keyserver-conn-types.js';
 import type {
@@ -27,7 +30,13 @@
     const { activityUpdates } = input;
     const requests: { [string]: { +updates: ActivityUpdate[] } } = {};
     for (const update of activityUpdates) {
-      const keyserverID = extractKeyserverIDFromID(update.threadID);
+      const optionalKeyserverID = extractKeyserverIDFromIDOptional(
+        update.threadID,
+      );
+      if (!optionalKeyserverID) {
+        continue;
+      }
+      const keyserverID: string = optionalKeyserverID;
       if (!requests[keyserverID]) {
         requests[keyserverID] = { updates: [] };
       }
diff --git a/lib/actions/link-actions.js b/lib/actions/link-actions.js
--- a/lib/actions/link-actions.js
+++ b/lib/actions/link-actions.js
@@ -1,4 +1,5 @@
 // @flow
+
 import * as React from 'react';
 
 import type { CallSingleKeyserverEndpoint } from '../keyserver-conn/call-single-keyserver-endpoint.js';
diff --git a/lib/actions/upload-actions.js b/lib/actions/upload-actions.js
--- a/lib/actions/upload-actions.js
+++ b/lib/actions/upload-actions.js
@@ -6,7 +6,7 @@
 
 import blobService from '../facts/blob-service.js';
 import type { CallSingleKeyserverEndpoint } from '../keyserver-conn/call-single-keyserver-endpoint.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import { useKeyserverCall } from '../keyserver-conn/keyserver-call.js';
 import type { CallKeyserverEndpoint } from '../keyserver-conn/keyserver-conn-types.js';
 import { type PerformHTTPMultipartUpload } from '../keyserver-conn/multipart-upload.js';
@@ -115,7 +115,9 @@
   ): ((input: DeleteUploadInput) => Promise<void>) =>
   async input => {
     const { id, keyserverOrThreadID } = input;
-    const keyserverID = extractKeyserverIDFromID(keyserverOrThreadID);
+    const keyserverID: string =
+      extractKeyserverIDFromIDOptional(keyserverOrThreadID) ??
+      keyserverOrThreadID;
     const requests = { [keyserverID]: { id } };
     await callKeyserverEndpoint('delete_upload', requests);
   };
@@ -224,7 +226,9 @@
     }
 
     // 3. Upload metadata to keyserver
-    const keyserverID = extractKeyserverIDFromID(keyserverOrThreadID);
+    const keyserverID: string =
+      extractKeyserverIDFromIDOptional(keyserverOrThreadID) ??
+      keyserverOrThreadID;
     const requests = {
       [keyserverID]: {
         blobHash,
diff --git a/lib/keyserver-conn/keyserver-auth.js b/lib/keyserver-conn/keyserver-auth.js
--- a/lib/keyserver-conn/keyserver-auth.js
+++ b/lib/keyserver-conn/keyserver-auth.js
@@ -4,7 +4,7 @@
 import * as React from 'react';
 
 import { useCallKeyserverEndpointContext } from './call-keyserver-endpoint-provider.react.js';
-import { extractKeyserverIDFromID } from './keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from './keyserver-call-utils.js';
 import {
   CANCELLED_ERROR,
   type CallKeyserverEndpoint,
@@ -46,7 +46,8 @@
   const calendarQuery = React.useMemo(() => {
     const filters = filterThreadIDsInFilterList(
       calendarFilters,
-      (threadID: string) => extractKeyserverIDFromID(threadID) === keyserverID,
+      (threadID: string) =>
+        extractKeyserverIDFromIDOptional(threadID) === keyserverID,
     );
     return {
       startDate: navInfo.startDate,
diff --git a/lib/keyserver-conn/keyserver-call-utils.js b/lib/keyserver-conn/keyserver-call-utils.js
--- a/lib/keyserver-conn/keyserver-call-utils.js
+++ b/lib/keyserver-conn/keyserver-call-utils.js
@@ -5,17 +5,29 @@
 import type { CalendarQuery } from '../types/entry-types.js';
 import type { NotDeletedFilter } from '../types/filter-types.js';
 
-function extractKeyserverIDFromID(id: string): string {
+function extractKeyserverIDFromIDOptional(id: string): ?string {
+  if (!id.includes('|')) {
+    return null;
+  }
   return id.split('|')[0];
 }
 
+function extractKeyserverIDFromID(id: string): string {
+  const keyserverID = extractKeyserverIDFromIDOptional(id);
+  invariant(keyserverID, 'Keyserver ID should be present');
+  return keyserverID;
+}
+
 function sortThreadIDsPerKeyserver(threadIDs: $ReadOnlyArray<string>): {
   +[keyserverID: string]: $ReadOnlyArray<string>,
 } {
   const results: { [string]: string[] } = {};
   for (const threadID of threadIDs) {
-    const keyserverID = extractKeyserverIDFromID(threadID);
-    invariant(keyserverID, 'keyserver data missing from thread id');
+    const optionalKeyserverID = extractKeyserverIDFromIDOptional(threadID);
+    if (!optionalKeyserverID) {
+      continue;
+    }
+    const keyserverID: string = optionalKeyserverID;
     if (results[keyserverID] === undefined) {
       results[keyserverID] = [];
     }
@@ -60,8 +72,8 @@
       }
     } else if (filter.type === 'threads') {
       for (const threadID of filter.threadIDs) {
-        const keyserverID = extractKeyserverIDFromID(threadID);
-        if (results[keyserverID] === undefined) {
+        const keyserverID = extractKeyserverIDFromIDOptional(threadID);
+        if (!keyserverID || results[keyserverID] === undefined) {
           continue;
         }
         let threadFilter = results[keyserverID].filters.find(
@@ -94,12 +106,14 @@
     return [];
   }
   const keyserverIDsSet = new Set<string>(keyserverIDs);
-  return threadIDs.filter(threadID =>
-    keyserverIDsSet.has(extractKeyserverIDFromID(threadID)),
-  );
+  return threadIDs.filter(threadID => {
+    const keyserverID = extractKeyserverIDFromIDOptional(threadID);
+    return keyserverID && keyserverIDsSet.has(keyserverID);
+  });
 }
 
 export {
+  extractKeyserverIDFromIDOptional,
   extractKeyserverIDFromID,
   sortThreadIDsPerKeyserver,
   sortCalendarQueryPerKeyserver,
diff --git a/lib/keyserver-conn/keyserver-call-utils.test.js b/lib/keyserver-conn/keyserver-call-utils.test.js
--- a/lib/keyserver-conn/keyserver-call-utils.test.js
+++ b/lib/keyserver-conn/keyserver-call-utils.test.js
@@ -4,6 +4,7 @@
   extractKeyserverIDFromID,
   sortCalendarQueryPerKeyserver,
   getThreadIDsForKeyservers,
+  extractKeyserverIDFromIDOptional,
 } from './keyserver-call-utils.js';
 import type { CalendarQuery } from '../types/entry-types';
 
@@ -14,9 +15,22 @@
     expect(extractKeyserverIDFromID(id)).toBe(keyserverID);
   });
 
-  it('should return <keyserverID> for <keyserverID>', () => {
+  it('should throw for non-keyserver ID', () => {
+    const id = '404';
+    expect(() => extractKeyserverIDFromID(id)).toThrow();
+  });
+});
+
+describe('extractKeyserverIDFromIDOptional', () => {
+  it('should return <keyserverID> for <keyserverID>|<number>', () => {
     const keyserverID = '404';
-    expect(extractKeyserverIDFromID(keyserverID)).toBe(keyserverID);
+    const id = keyserverID + '|1234';
+    expect(extractKeyserverIDFromIDOptional(id)).toBe(keyserverID);
+  });
+
+  it('should return null for non-keyserver ID', () => {
+    const id = '404';
+    expect(extractKeyserverIDFromIDOptional(id)).toBe(null);
   });
 });
 
diff --git a/lib/reducers/calendar-filters-reducer.js b/lib/reducers/calendar-filters-reducer.js
--- a/lib/reducers/calendar-filters-reducer.js
+++ b/lib/reducers/calendar-filters-reducer.js
@@ -18,7 +18,7 @@
   legacyLogInActionTypes,
   legacyKeyserverRegisterActionTypes,
 } from '../actions/user-actions.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
 import {
   filteredThreadIDs,
@@ -220,8 +220,10 @@
   keyserverIDs: $ReadOnlyArray<string>,
 ): $ReadOnlyArray<CalendarFilter> {
   const keyserverIDsSet = new Set<string>(keyserverIDs);
-  const filterCondition = (threadID: string) =>
-    !keyserverIDsSet.has(extractKeyserverIDFromID(threadID));
+  const filterCondition = (threadID: string) => {
+    const keyserverID = extractKeyserverIDFromIDOptional(threadID);
+    return !keyserverID || !keyserverIDsSet.has(keyserverID);
+  };
 
   return filterThreadIDsInFilterList(state, filterCondition);
 }
diff --git a/lib/reducers/draft-reducer.js b/lib/reducers/draft-reducer.js
--- a/lib/reducers/draft-reducer.js
+++ b/lib/reducers/draft-reducer.js
@@ -6,7 +6,7 @@
   updateDraftActionType,
 } from '../actions/draft-actions.js';
 import { deleteKeyserverAccountActionTypes } from '../actions/user-actions.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
 import type { DraftStore, DraftStoreOperation } from '../types/draft-types.js';
 import type { BaseAction } from '../types/redux-types.js';
@@ -25,7 +25,8 @@
   const drafts: { [key: string]: string } = {};
   const ids: string[] = [];
   for (const key in draftStore.drafts) {
-    if (keyserverIDsSet.has(extractKeyserverIDFromID(key))) {
+    const keyserverID = extractKeyserverIDFromIDOptional(key);
+    if (keyserverID && keyserverIDsSet.has(keyserverID)) {
       ids.push(key);
     } else {
       drafts[key] = draftStore.drafts[key];
diff --git a/lib/reducers/invite-links-reducer.js b/lib/reducers/invite-links-reducer.js
--- a/lib/reducers/invite-links-reducer.js
+++ b/lib/reducers/invite-links-reducer.js
@@ -6,7 +6,7 @@
   fetchPrimaryInviteLinkActionTypes,
 } from '../actions/link-actions.js';
 import { deleteKeyserverAccountActionTypes } from '../actions/user-actions.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
 import type { InviteLinksStore, CommunityLinks } from '../types/link-types.js';
 import type { BaseAction } from '../types/redux-types.js';
@@ -60,7 +60,8 @@
     const keyserverIDsSet = new Set<string>(action.payload.keyserverIDs);
     const newLinks: { [communityID: string]: CommunityLinks } = {};
     for (const linkID in state.links) {
-      if (!keyserverIDsSet.has(extractKeyserverIDFromID(linkID))) {
+      const keyserverID = extractKeyserverIDFromIDOptional(linkID);
+      if (!keyserverID || !keyserverIDsSet.has(keyserverID)) {
         newLinks[linkID] = state.links[linkID];
       }
     }
@@ -75,7 +76,7 @@
     const { keyserverID } = action.payload;
     const newLinks: { [communityID: string]: CommunityLinks } = {};
     for (const linkID in state.links) {
-      if (extractKeyserverIDFromID(linkID) !== keyserverID) {
+      if (extractKeyserverIDFromIDOptional(linkID) !== keyserverID) {
         newLinks[linkID] = state.links[linkID];
       }
     }
diff --git a/lib/reducers/keyserver-reducer.js b/lib/reducers/keyserver-reducer.js
--- a/lib/reducers/keyserver-reducer.js
+++ b/lib/reducers/keyserver-reducer.js
@@ -25,7 +25,7 @@
   legacyKeyserverRegisterActionTypes,
   legacyLogInActionTypes,
 } from '../actions/user-actions.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import {
   setNewSessionActionType,
   updateConnectionStatusActionType,
@@ -222,7 +222,7 @@
       const calendarFilters = filterThreadIDsInFilterList(
         action.payload.calendarResult.calendarQuery.filters,
         (threadID: string) =>
-          extractKeyserverIDFromID(threadID) === keyserverID,
+          extractKeyserverIDFromIDOptional(threadID) === keyserverID,
       );
       operations.push({
         type: 'replace_keyserver',
@@ -729,7 +729,7 @@
               filters: filterThreadIDsInFilterList(
                 action.payload.calendarQuery.filters,
                 (threadID: string) =>
-                  extractKeyserverIDFromID(threadID) === keyserverID,
+                  extractKeyserverIDFromIDOptional(threadID) === keyserverID,
               ),
             },
           },
diff --git a/lib/reducers/thread-activity-reducer.js b/lib/reducers/thread-activity-reducer.js
--- a/lib/reducers/thread-activity-reducer.js
+++ b/lib/reducers/thread-activity-reducer.js
@@ -17,7 +17,7 @@
 } from '../actions/thread-actions.js';
 import { fetchPendingUpdatesActionTypes } from '../actions/update-actions.js';
 import { deleteKeyserverAccountActionTypes } from '../actions/user-actions.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
 import {
   threadActivityStoreOpsHandlers,
@@ -183,7 +183,8 @@
     const keyserverIDsSet = new Set<string>(action.payload.keyserverIDs);
 
     for (const threadID in state) {
-      if (!keyserverIDsSet.has(extractKeyserverIDFromID(threadID))) {
+      const keyserverID = extractKeyserverIDFromIDOptional(threadID);
+      if (!keyserverID || !keyserverIDsSet.has(keyserverID)) {
         continue;
       }
       threadIDsToRemove.push(threadID);
@@ -208,7 +209,7 @@
     const { keyserverID } = action.payload;
 
     for (const threadID in state) {
-      if (extractKeyserverIDFromID(threadID) !== keyserverID) {
+      if (extractKeyserverIDFromIDOptional(threadID) !== keyserverID) {
         continue;
       }
       threadIDsToRemove.push(threadID);
diff --git a/lib/reducers/user-reducer.js b/lib/reducers/user-reducer.js
--- a/lib/reducers/user-reducer.js
+++ b/lib/reducers/user-reducer.js
@@ -23,7 +23,7 @@
   setUserSettingsActionTypes,
   updateUserAvatarActionTypes,
 } from '../actions/user-actions.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import { setNewSessionActionType } from '../keyserver-conn/keyserver-conn-types.js';
 import {
   convertUserInfosToReplaceUserOps,
@@ -310,7 +310,9 @@
     if (action.type === joinThreadActionTypes.success) {
       keyserverID = action.payload.keyserverID;
     } else {
-      keyserverID = extractKeyserverIDFromID(action.payload.newThreadID);
+      keyserverID = extractKeyserverIDFromIDOptional(
+        action.payload.newThreadID,
+      );
     }
     if (keyserverID !== authoritativeKeyserverID()) {
       return [state, [], []];
diff --git a/lib/selectors/thread-selectors.js b/lib/selectors/thread-selectors.js
--- a/lib/selectors/thread-selectors.js
+++ b/lib/selectors/thread-selectors.js
@@ -18,7 +18,7 @@
 } from './calendar-filter-selectors.js';
 import { relativeMemberInfoSelectorForMembersOfThread } from './user-selectors.js';
 import genesis from '../facts/genesis.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import {
   getAvatarForThread,
   getRandomDefaultEmojiAvatar,
@@ -251,7 +251,7 @@
   (state: BaseAppState<>) => state.threadStore.threadInfos,
   (threadInfos: RawThreadInfos): { +[keyserverID: string]: number } => {
     const keyserverToThreads = _groupBy(threadInfo =>
-      extractKeyserverIDFromID(threadInfo.id),
+      extractKeyserverIDFromIDOptional(threadInfo.id),
     )(
       values(threadInfos).filter(threadInfo =>
         threadInHomeChatList(threadInfo),
diff --git a/lib/shared/message-utils.js b/lib/shared/message-utils.js
--- a/lib/shared/message-utils.js
+++ b/lib/shared/message-utils.js
@@ -11,7 +11,7 @@
 import { messageSpecs } from './messages/message-specs.js';
 import { threadIsGroupChat } from './thread-utils.js';
 import { useStringForUser } from '../hooks/ens-cache.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import { contentStringForMediaArray } from '../media/media-utils.js';
 import type { ChatMessageInfoItem } from '../selectors/chat-selectors.js';
 import { threadInfoSelector } from '../selectors/thread-selectors.js';
@@ -661,11 +661,12 @@
   const timePerKeyserver: { [keyserverID: string]: number } = {};
 
   for (const messageInfo of messageInfos) {
-    const keyserverID = extractKeyserverIDFromID(messageInfo.threadID);
+    const keyserverID = extractKeyserverIDFromIDOptional(messageInfo.threadID);
 
     if (
-      !timePerKeyserver[keyserverID] ||
-      timePerKeyserver[keyserverID] < messageInfo.time
+      keyserverID &&
+      (!timePerKeyserver[keyserverID] ||
+        timePerKeyserver[keyserverID] < messageInfo.time)
     ) {
       timePerKeyserver[keyserverID] = messageInfo.time;
     }
diff --git a/lib/shared/state-sync/entries-state-sync-spec.js b/lib/shared/state-sync/entries-state-sync-spec.js
--- a/lib/shared/state-sync/entries-state-sync-spec.js
+++ b/lib/shared/state-sync/entries-state-sync-spec.js
@@ -4,7 +4,7 @@
 import { createSelector } from 'reselect';
 
 import type { StateSyncSpec, BoundStateSyncSpec } from './state-sync-spec.js';
-import { extractKeyserverIDFromID } from '../../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../../keyserver-conn/keyserver-call-utils.js';
 import {
   type CalendarQuery,
   type RawEntryInfos,
@@ -64,10 +64,12 @@
   ) {
     const keyserverID = action.payload.keyserverID;
     const filteredBeforeStateCheck = values(beforeStateCheck).filter(
-      entry => entry.id && extractKeyserverIDFromID(entry.id) === keyserverID,
+      entry =>
+        entry.id && extractKeyserverIDFromIDOptional(entry.id) === keyserverID,
     );
     const filteredAfterStateCheck = values(afterStateCheck).filter(
-      entry => entry.id && extractKeyserverIDFromID(entry.id) === keyserverID,
+      entry =>
+        entry.id && extractKeyserverIDFromIDOptional(entry.id) === keyserverID,
     );
 
     const calendarQuery = action.payload.calendarQuery;
@@ -111,7 +113,8 @@
   keyserverID: string,
 ) {
   const filteredEntries = values(entryInfos).filter(
-    entry => entry.id && extractKeyserverIDFromID(entry.id) === keyserverID,
+    entry =>
+      entry.id && extractKeyserverIDFromIDOptional(entry.id) === keyserverID,
   );
   const filteredEntryInfos = filterRawEntryInfosByCalendarQuery(
     serverEntryInfosObject(filteredEntries),
@@ -127,7 +130,8 @@
   keyserverID: string,
 ) {
   const filteredEntries = values(entryInfos).filter(
-    entry => entry.id && extractKeyserverIDFromID(entry.id) === keyserverID,
+    entry =>
+      entry.id && extractKeyserverIDFromIDOptional(entry.id) === keyserverID,
   );
   const filteredEntryInfos = filterRawEntryInfosByCalendarQuery(
     serverEntryInfosObject(filteredEntries),
diff --git a/lib/shared/state-sync/threads-state-sync-spec.js b/lib/shared/state-sync/threads-state-sync-spec.js
--- a/lib/shared/state-sync/threads-state-sync-spec.js
+++ b/lib/shared/state-sync/threads-state-sync-spec.js
@@ -5,7 +5,7 @@
 import { createSelector } from 'reselect';
 
 import type { StateSyncSpec, BoundStateSyncSpec } from './state-sync-spec.js';
-import { extractKeyserverIDFromID } from '../../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../../keyserver-conn/keyserver-call-utils.js';
 import type { CalendarQuery } from '../../types/entry-types.js';
 import type { RawThreadInfo } from '../../types/minimally-encoded-thread-permissions-types.js';
 import type { AppState } from '../../types/redux-types.js';
@@ -41,14 +41,16 @@
       ? (query: CalendarQuery, keyserverID: string) =>
           combineUnorderedHashes(
             entries(threadHashes)
-              .filter(([id]) => extractKeyserverIDFromID(id) === keyserverID)
+              .filter(
+                ([id]) => extractKeyserverIDFromIDOptional(id) === keyserverID,
+              )
               .map(([, threadHash]) => threadHash),
           )
       : () => null,
     getIDs: threadHashingComplete
       ? (query: CalendarQuery, keyserverID: string) =>
           Object.keys(threadHashes)
-            .filter(id => extractKeyserverIDFromID(id) === keyserverID)
+            .filter(id => extractKeyserverIDFromIDOptional(id) === keyserverID)
             .map(id => id.split('|')[1])
       : () => null,
     canSyncState: () => threadHashingComplete,
@@ -73,7 +75,7 @@
   ) {
     const keyserverID = action.payload.keyserverID;
     const filter = _pickBy(
-      thread => extractKeyserverIDFromID(thread.id) === keyserverID,
+      thread => extractKeyserverIDFromIDOptional(thread.id) === keyserverID,
     );
     const filteredBeforeStateCheck = filter(beforeStateCheck);
     const filteredAfterStateCheck = filter(afterStateCheck);
diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js
--- a/lib/shared/thread-utils.js
+++ b/lib/shared/thread-utils.js
@@ -15,7 +15,7 @@
 import ashoat from '../facts/ashoat.js';
 import genesis from '../facts/genesis.js';
 import { useLoggedInUserInfo } from '../hooks/account-hooks.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import {
   hasPermission,
   permissionsToBitmaskHex,
@@ -349,7 +349,7 @@
   if (threadInfo.community !== genesis().id) {
     return threadInfo.members;
   }
-  const adminID = extractKeyserverIDFromID(threadInfo.id);
+  const adminID = extractKeyserverIDFromIDOptional(threadInfo.id);
 
   return threadInfo.members.filter(
     member => member.id !== adminID || member.role,
diff --git a/lib/socket/activity-handler.react.js b/lib/socket/activity-handler.react.js
--- a/lib/socket/activity-handler.react.js
+++ b/lib/socket/activity-handler.react.js
@@ -7,7 +7,7 @@
   updateActivityActionTypes,
   useUpdateActivity,
 } from '../actions/activity-actions.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import { connectionSelector } from '../selectors/keyserver-selectors.js';
 import { getMostRecentNonLocalMessageID } from '../shared/message-utils.js';
 import { threadIsPending } from '../shared/thread-utils.js';
@@ -76,7 +76,7 @@
       if (
         prevActiveThread &&
         !isPrevActiveThreadPending &&
-        extractKeyserverIDFromID(prevActiveThread) === keyserverID
+        extractKeyserverIDFromIDOptional(prevActiveThread) === keyserverID
       ) {
         activityUpdates.push({
           focus: false,
@@ -87,7 +87,7 @@
       if (
         activeThread &&
         !isActiveThreadPending &&
-        extractKeyserverIDFromID(activeThread) === keyserverID
+        extractKeyserverIDFromIDOptional(activeThread) === keyserverID
       ) {
         activityUpdates.push({
           focus: true,
@@ -103,7 +103,7 @@
       prevConnectionStatus === 'connected' &&
       activeThread &&
       !isActiveThreadPending &&
-      extractKeyserverIDFromID(activeThread) === keyserverID
+      extractKeyserverIDFromIDOptional(activeThread) === keyserverID
     ) {
       // When the server closes a socket it also deletes any activity rows
       // associated with that socket's session. If that activity is still
diff --git a/lib/socket/calendar-query-handler.react.js b/lib/socket/calendar-query-handler.react.js
--- a/lib/socket/calendar-query-handler.react.js
+++ b/lib/socket/calendar-query-handler.react.js
@@ -9,7 +9,7 @@
   useUpdateCalendarQuery,
 } from '../actions/entry-actions.js';
 import type { UpdateCalendarQueryInput } from '../actions/entry-actions.js';
-import { extractKeyserverIDFromID } from '../keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from '../keyserver-conn/keyserver-call-utils.js';
 import { filterThreadIDsInFilterList } from '../reducers/calendar-filters-reducer.js';
 import { timeUntilCalendarRangeExpiration } from '../selectors/nav-selectors.js';
 import { useIsAppForegrounded } from '../shared/lifecycle-utils.js';
@@ -150,7 +150,7 @@
         filters: filterThreadIDsInFilterList(
           query.filters,
           (threadID: string) =>
-            extractKeyserverIDFromID(threadID) === keyserverID,
+            extractKeyserverIDFromIDOptional(threadID) === keyserverID,
         ),
       };
     }, [currentCalendarQuery, keyserverID]);
diff --git a/native/calendar/entry.react.js b/native/calendar/entry.react.js
--- a/native/calendar/entry.react.js
+++ b/native/calendar/entry.react.js
@@ -26,7 +26,7 @@
   useDeleteEntry,
   useSaveEntry,
 } from 'lib/actions/entry-actions.js';
-import { extractKeyserverIDFromID } from 'lib/keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from 'lib/keyserver-conn/keyserver-call-utils.js';
 import { registerFetchKey } from 'lib/reducers/loading-reducer.js';
 import { connectionSelector } from 'lib/selectors/keyserver-selectors.js';
 import { colorIsDark } from 'lib/shared/color-utils.js';
@@ -809,11 +809,18 @@
     const { threadInfo: unresolvedThreadInfo, ...restProps } = props;
     const threadInfo = useResolvedThreadInfo(unresolvedThreadInfo);
 
-    const keyserverID = extractKeyserverIDFromID(threadInfo.id);
-    const connection = useSelector(connectionSelector(keyserverID));
+    const keyserverID = extractKeyserverIDFromIDOptional(threadInfo.id);
+    const connection = useSelector(state => {
+      if (!keyserverID) {
+        return {
+          status: 'connected',
+        };
+      }
+      return connectionSelector(keyserverID)(state);
+    });
     invariant(
       connection,
-      `keyserver ${keyserverID} missing from keyserverStore`,
+      `keyserver ${keyserverID ?? 'null'} missing from keyserverStore`,
     );
     const online = connection.status === 'connected';
 
diff --git a/native/chat/settings/thread-settings-push-notifs.react.js b/native/chat/settings/thread-settings-push-notifs.react.js
--- a/native/chat/settings/thread-settings-push-notifs.react.js
+++ b/native/chat/settings/thread-settings-push-notifs.react.js
@@ -4,7 +4,7 @@
 import { Platform, TouchableOpacity, View } from 'react-native';
 import Linking from 'react-native/Libraries/Linking/Linking.js';
 
-import { extractKeyserverIDFromID } from 'lib/keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from 'lib/keyserver-conn/keyserver-call-utils.js';
 import { deviceTokenSelector } from 'lib/selectors/keyserver-selectors.js';
 import { threadSettingsNotificationsCopy } from 'lib/shared/thread-settings-notifications-utils.js';
 import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
@@ -143,8 +143,13 @@
   React.memo<BaseProps>(function ConnectedThreadSettingsPushNotifs(
     props: BaseProps,
   ) {
-    const keyserverID = extractKeyserverIDFromID(props.threadInfo.id);
-    const deviceToken = useSelector(deviceTokenSelector(keyserverID));
+    const keyserverID = extractKeyserverIDFromIDOptional(props.threadInfo.id);
+    const deviceToken = useSelector(state => {
+      if (!keyserverID) {
+        return state.tunnelbrokerDeviceToken;
+      }
+      return deviceTokenSelector(keyserverID)(state);
+    });
     const hasPushPermissions =
       deviceToken !== null && deviceToken !== undefined;
     const styles = useStyles(unboundStyles);
diff --git a/native/push/push-handler.react.js b/native/push/push-handler.react.js
--- a/native/push/push-handler.react.js
+++ b/native/push/push-handler.react.js
@@ -17,7 +17,10 @@
   useSetDeviceTokenFanout,
 } from 'lib/actions/device-actions.js';
 import { saveMessagesActionType } from 'lib/actions/message-actions.js';
-import { extractKeyserverIDFromID } from 'lib/keyserver-conn/keyserver-call-utils.js';
+import {
+  extractKeyserverIDFromID,
+  extractKeyserverIDFromIDOptional,
+} from 'lib/keyserver-conn/keyserver-call-utils.js';
 import {
   deviceTokensSelector,
   allUpdatesCurrentAsOfSelector,
@@ -624,7 +627,7 @@
     }
 
     const keyserverIDToMessageInfos = _groupBy(messageInfos =>
-      extractKeyserverIDFromID(messageInfos.threadID),
+      extractKeyserverIDFromIDOptional(messageInfos.threadID),
     )(rawMessageInfos);
 
     for (const keyserverID in keyserverIDToMessageInfos) {
diff --git a/web/calendar/entry.react.js b/web/calendar/entry.react.js
--- a/web/calendar/entry.react.js
+++ b/web/calendar/entry.react.js
@@ -17,7 +17,7 @@
   type PushModal,
   useModalContext,
 } from 'lib/components/modal-provider.react.js';
-import { extractKeyserverIDFromID } from 'lib/keyserver-conn/keyserver-call-utils.js';
+import { extractKeyserverIDFromIDOptional } from 'lib/keyserver-conn/keyserver-call-utils.js';
 import { connectionSelector } from 'lib/selectors/keyserver-selectors.js';
 import { threadInfoSelector } from 'lib/selectors/thread-selectors.js';
 import { colorIsDark } from 'lib/shared/color-utils.js';
@@ -473,13 +473,13 @@
       threadPermissions.EDIT_ENTRIES,
     );
     const calendarQuery = useSelector(nonThreadCalendarQuery);
-    const keyserverID = extractKeyserverIDFromID(threadID);
-    const connection = useSelector(connectionSelector(keyserverID));
-    invariant(
-      connection,
-      `keyserver ${keyserverID} missing from keyserverStore`,
-    );
-    const online = connection.status === 'connected';
+    const keyserverID = extractKeyserverIDFromIDOptional(threadID);
+    const online = useSelector(state => {
+      if (!keyserverID) {
+        return true;
+      }
+      return connectionSelector(keyserverID)(state) === 'connected';
+    });
     const callCreateEntry = useCreateEntry();
     const callSaveEntry = useSaveEntry();
     const callDeleteEntry = useDeleteEntry();