{"record":{"id":"5389e8686eca4f04","repo":"laurent22/joplin","slug":"the-active-profile-cannot-be-deleted-switch-to-a","errorCode":null,"errorMessage":"The active profile cannot be deleted. Switch to a different profile and try again.","messagePattern":"The active profile cannot be deleted\\. Switch to a different profile and try again\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/app-mobile/components/ProfileSwitcher/utils/deleteProfile.ts","lineNumber":21,"sourceCode":"import { deleteProfileById, getCurrentProfile, isSubProfile } from '@joplin/lib/services/profileConfig';\nimport Setting from '@joplin/lib/models/Setting';\nimport shim, { MessageBoxType } from '@joplin/lib/shim';\nimport Logger from '@joplin/utils/Logger';\nimport resolvePathWithinDir from '@joplin/lib/utils/resolvePathWithinDir';\nimport DatabaseDriver from '@joplin/lib/database-driver';\nimport { _ } from '@joplin/lib/locale';\n\nconst logger = Logger.create('deleteProfile');\n\ninterface DeleteProfileOptions {\n\ttoDelete: Profile;\n\tprofileConfig: ProfileConfig;\n\tdatabaseDriver: DatabaseDriver;\n}\n\nconst deleteProfile = async (options: DeleteProfileOptions) => {\n\tlogger.info('Deleting profile config', options.toDelete.id);\n\tif (options.toDelete.id === options.profileConfig.currentProfileId) throw new Error(_('The active profile cannot be deleted. Switch to a different profile and try again.'));\n\tconst subProfile = isSubProfile(options.toDelete);\n\n\t// Deleting the default profile must be handled differently. We can't delete the whole directory because it contains other profiles and global settings\n\tif (subProfile) {\n\t\tconst newConfig = deleteProfileById(options.profileConfig, options.toDelete.id);\n\t\t// Save the profile config early. If the later deletion steps fail, this prevents the user from\n\t\t// opening a partially-deleted profile. The default profile does not get deleted from the list,\n\t\t// but the data will be cleared\n\t\tawait saveProfileConfig(newConfig);\n\t}\n\n\t// Retrieve and validate both the database name and resources directory\n\t// **before** doing any deletion.\n\tconst databaseName = getTargetDatabaseName(options);\n\tconst resourcesDir = getTargetResourceDirectory(options);\n\tconst pluginDataDir = getTargetPluginDataDirectory(options);\n\n\tlogger.info('Deleting database', databaseName);","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/laurent22/joplin/blob/2654b33620775080d1d59c552259d41e33dad3d2/packages/app-mobile/components/ProfileSwitcher/utils/deleteProfile.ts#L3-L39","documentation":"Thrown by `deleteProfile` in the mobile profile switcher when the profile being deleted (`options.toDelete.id`) equals the currently active profile id (`options.profileConfig.currentProfileId`). Deleting the active profile would leave the app with no current profile, so the operation is blocked before any filesystem or database work begins.","triggerScenarios":"Calling `deleteProfile({ toDelete, profileConfig, databaseDriver })` where `toDelete.id === profileConfig.currentProfileId`. Reached from the profile switcher UI when the user taps delete on the profile the app is currently running under.","commonSituations":"Single-profile installs where the user tries to delete the only/active profile; UI bug that does not disable the delete button for the active row; stale `profileConfig` snapshot read before a profile switch completed.","solutions":["Switch to a different profile first (`switchProfile`), then delete the inactive one.","In the UI, disable the delete control for the row whose id equals `currentProfileId`.","Pre-check before invoking: `if (toDelete.id === profileConfig.currentProfileId) { /* warn user */ return; }`.","Reload `profileConfig` immediately before the delete call to avoid a stale snapshot."],"exampleFix":"// before\ndeleteProfile({ toDelete, profileConfig, databaseDriver });\n\n// after\nif (toDelete.id === profileConfig.currentProfileId) {\n  await showDialog(_('Switch to a different profile before deleting this one.'));\n  return;\n}\nawait deleteProfile({ toDelete, profileConfig, databaseDriver });","handlingStrategy":"validation","validationCode":"const isActive = (toDelete, profileConfig) =>\n  toDelete.id === profileConfig.currentProfileId;\n\nif (isActive(toDelete, profileConfig)) {\n  await showDialog(_('Switch to a different profile before deleting this one.'));\n  return;\n}\nawait deleteProfile({ toDelete, profileConfig, databaseDriver });","typeGuard":"null","tryCatchPattern":"try {\n  await deleteProfile({ toDelete, profileConfig, databaseDriver });\n} catch (error) {\n  if (/active profile cannot be deleted/i.test(error.message)) {\n    await showSwitchProfilePrompt();\n    return;\n  }\n  throw error;\n}","preventionTips":["Disable the delete control in the UI for the active profile row.","Reload profileConfig immediately before deleting to avoid stale snapshots.","Switch profiles programmatically before deleting the previously-active one."],"tags":["profiles","validation","mobile","precondition"],"backgroundTag":null,"analyzedSha":"2654b33620775080d1d59c552259d41e33dad3d2","analyzedAt":"2026-08-12T14:26:46.263Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}