Why Running Two Native WhatsApp Apps on a Mac Is So Difficult

Two phone numbers, two WhatsApp accounts, one Mac. A closer look at what happens when you try to give each account its own native app.

Two green WhatsApp speech-bubble sculptures beneath the words TWO ACCOUNTS. ONE MAC.

WhatsApp needs very little introduction. For many people, it is where family conversations, work messages, school groups, and customer questions all end up. Eventually, keeping those parts of life separate becomes useful enough to justify a second phone number and a second account.

WhatsApp recognizes that need. Its multiple-account feature supports two accounts on a mobile device, although the same feature is not available on desktop linked devices.

So you sit down at your Mac with a reasonable expectation. You already have two accounts. You already have a native WhatsApp app. Surely there must be a way to open another copy and sign into the other account.

Then you start looking for one.

The second app looks a little different

If you try this with Parall, you might notice something. The WhatsApp shortcut gives you a separate window and a separate session, but it opens WhatsApp Web inside a lightweight web app. It does not launch another copy of the installed native WhatsApp client.

The window uses the system WebKit engine. That makes the wrapper a native Mac application, but the messaging interface inside it is still the web version of WhatsApp. Those are different things, and Parall identifies WhatsApp support as web app support.

That may be perfectly useful. It may also be exactly what you were trying to avoid.

You wanted the native client, so you keep searching. Perhaps another launcher does it. Perhaps duplicating the app in Finder will work. Perhaps there is a Terminal command or an obscure setting that everyone else has missed.

As the developer of Parall, I understand that expectation. Explaining why WhatsApp is different takes more than saying that it is incompatible. I wanted to look at what the Mac app actually does.

Two processes are only the beginning

Running another process and giving that process another account are separate problems.

For two accounts to work independently, the instances need separate storage for their settings, sessions, and databases. Launching the same executable twice does not automatically provide that separation.

Some apps accept a profile directory. Others use locations that a launcher can redirect. A sandboxed app can also support multiple profiles if its own design allows it. Sandboxing is not a universal prohibition on multiple accounts.

WhatsApp's storage arrangement is more involved than a folder a launcher can rename.

For this investigation, I inspected the installed /Applications/WhatsApp.app, version 26.43.75, build 1104102785, on November 3, 2026. I read its metadata, entitlements, strings, and ARM64 disassembly. I did not modify the app or use a WhatsApp account to test a patched client.

The main executable declares the App Sandbox entitlement. It also declares several shared application groups, including these two.

com.apple.security.app-sandbox = true
group.net.whatsapp.WhatsApp.shared
group.net.whatsapp.WhatsApp.private

The notification and Intents extensions declare those groups too. There are several cooperating components with an established storage identity, rather than one isolated executable whose data can simply move somewhere else.

Apple documents that macOS associates a sandbox container with an app's code signature. Shared app group containers have their own access rules. Changing a launcher environment variable such as HOME does not create a new identity for all those components or redirect the app group identifiers compiled into the app.

That is the first obstacle. A second process can still lead back to the same account data.

What if the copy had a different identity?

There is another approach worth considering. Copy the app, change its bundle identifier, and sign the copy again. With some software, repackaging can be part of creating an independent instance.

With WhatsApp, changing the name under the Dock icon would barely begin the job. Its bundle identifier is net.whatsapp.WhatsApp. Its main app, extensions, shared groups, and signing requirements are connected in ways that a new display name does not change.

The inspected main executable also has the hardened runtime enabled. Its entitlements do not include exceptions allowing DYLD_ environment variables or disabling library validation. Under Apple's hardened runtime rules, these protections restrict common ways to inject code or load replacement libraries into the original app.

Re-signing a modified copy would replace the original signature. It would not preserve Meta's identity, and keeping the same entitlement names would not guarantee that every component could still access the resources it expects.

There is a distinction I want to keep precise here. These are observable signing and runtime protections. My inspection did not establish a separate Meta anti-tamper mechanism that rejects every possible re-signed copy. Finding signing-related code or an integrity-related string would not, by itself, prove such a mechanism exists.

The app also bundles update components, and the main app declares communication exceptions for its update services. Updating the installed application is a separate job from creating independent account storage. The updater executables I inspected also declare sandbox entitlements, so I cannot describe this particular build's updater as an unsandboxed escape route.

Even if you solved the storage and identity problems, there was still another question. Would WhatsApp actually agree to keep the second instance open?

"Another instance of WhatsApp is already running"

Inside the app's Mac plugin, I found a method with a remarkably direct name.

isDuplicateApplicationInstanceRunning

This was more useful than a suggestive string. The disassembly showed what the method does. It reads the main bundle's identifier, passes it to NSRunningApplication.runningApplications(withBundleIdentifier:), and checks whether the returned array contains more than one application.

In simplified pseudocode, the test is this.

let identifier = Bundle.main.bundleIdentifier
let instances = NSRunningApplication.runningApplications(
    withBundleIdentifier: identifier
)
return instances.count > 1

This is a reconstruction of the observed logic, not Meta's source code. The real method also handles a missing bundle identifier.

The main executable contains WhatsAppAppDelegate.detectDuplicateInstance. I traced that routine to the plugin check and then to an alert-and-quit method. The English alert title embedded in that path is exactly this.

Another instance of WhatsApp is already running

The accompanying message is also explicit.

You can't run two instances of WhatsApp. Please quit the other instance and try again.

The plugin method quitCurrentApplicationInstanceWithAlertMessage:informativeText: sets the alert text, presents the alert, and calls terminate: on the application.

If you have encountered either of those messages on your Mac, this is the behavior behind them. WhatsApp has its own duplicate-instance detection and termination path. Getting macOS to create another process does not remove that check.

There is an important limit to what it proves. The check I traced uses the running app's bundle identifier. It does not prove that every renamed, re-signed, patched, or separately virtualized version must fail. It explains why another ordinary launch of the same app identity runs into an explicit refusal.

And changing the identity to get past that particular test would leave the earlier storage and signing problems waiting for you.

What it takes to make a native clone

Following these obstacles leads to a more invasive possibility. A modified copy could use a new identity and redirect the APIs that locate its data. Examining that approach also reveals what would have to change around the app.

Giving the copied app a new bundle identifier would address the first problem. WhatsApp's duplicate check asks how many apps are running with the current identifier, so a unique one should avoid that particular check without patching its instructions. That follows from WhatsApp's own code, although I did not launch a clone to test it. Nested components would also need their identities considered.

One way to make runtime redirection possible is to remove the embedded provisioning profile, replace the signature, leave com.apple.security.app-sandbox out of the new entitlements, and clear the hardened runtime flags. The original app group names could remain in the entitlements while their storage locations are redirected inside the process.

That would remove restrictions that stand in the way of the modification. It would also remove their protection from the copy.

A library injected through DYLD_INSERT_LIBRARIES, configured in the app's LSEnvironment, can replace NSFileManager.containerURLForSecurityApplicationGroupIdentifier:. When WhatsApp asks for a shared container, the replacement can return a directory inside the clone's own storage.

Shared preferences would need attention too. Replacing NSUserDefaults methods such as initWithSuiteName:, addSuiteNamed:, and removeSuiteNamed: can redirect preference suites. The app could keep asking for its familiar group names while receiving different storage locations.

Keychain access is another boundary. A compatibility layer can intercept these four functions.

SecItemAdd
SecItemCopyMatching
SecItemUpdate
SecItemDelete

Redirecting those calls into a separate store would give the clone another place to keep its credentials. The app would still call the familiar API, with a different implementation underneath.

These techniques provide ways around the individual obstacles. They also change more than the location of a database. Before treating that as a solution, we need to follow what happens to the app's protections.

What removing those protections changes

Removing App Sandbox is a security change, even if the reason is to make storage redirection easier. Apple describes the sandbox as a way to limit the damage a compromised app can cause. Without that boundary, an exploit in the modified client could reach resources that its original sandbox would have denied. Other macOS permissions still apply, so this does not grant unlimited access to the whole computer.

Disabling hardened runtime removes another layer. Its protections help resist code injection, library hijacking, and process memory tampering. Clearing those protections enables the injected library, but also changes the conditions under which other code can interfere with the process. These changes affect the clone, not the Mac's system-wide security settings.

The injected library is also part of what you must trust. It runs inside the messaging process and participates directly in finding account data and handling Keychain requests. A second Dock icon says nothing about the quality of that additional code.

This approach deliberately gives up defenses that the official app ships with. It does not mean a compromise has already happened. For an application holding private conversations and business messages, accepting that weaker starting point is a substantial cost for a second account.

What happens when Keychain becomes an ordinary file

A replacement that accepts the same Keychain function calls can still store data very differently from the macOS Keychain. This is where separating accounts can quietly become a question of protecting credentials.

Consider a replacement that serializes the intercepted items into a binary property list such as keychain.plist, then sets file permissions to 0600. That would give the owning user permission to read and write the file. Neither serialization nor those permissions add encryption.

Binary does not mean encrypted. It is a storage format. The values remain as supplied by the calling app unless the replacement explicitly encrypts them.

Owner-only file permissions are useful, but they do not distinguish between applications running as that owner. An unsandboxed process running as the same user, with access to the directory, could read the file without passing the Keychain's checks. Sensitive values passed into such a replacement would depend on ordinary file access restrictions and any protection the calling app adds itself.

Apple's explanation of macOS Keychain implementations describes access controls based on trusted applications or signed access groups. Replacing those operations with a plist does not preserve that security model.

The contents matter too. A value could already be encrypted before it reaches the replacement, so an unencrypted plist would not prove that every stored value is readable plaintext. This concerns local credential storage, and does not establish a break in WhatsApp's end-to-end encryption. Different replacement designs can provide different protections.

I would not treat an unencrypted file used to emulate Keychain operations as equivalent protection for a messaging account.

A working modification would have to keep working

Runtime redirection also creates a maintenance obligation. The injected code depends on the app continuing to use the storage and Keychain interfaces it intercepts. An update that changes those paths can break the clone's assumptions. A missed path could undermine account separation even if the app still opens.

A successful result on one version therefore needs more than another launch test after an update. Account separation, session persistence, and cooperating components need checking too. This does not necessarily require a completely new hack every time, but it is continuing compatibility work around behavior that Meta has not exposed as a supported multiple-account interface.

There is also the account itself. WhatsApp warns that unofficial versions can lead to temporary bans and, in some circumstances, permanent bans. That warning does not establish the probability of a ban for any particular Mac modification. I cannot honestly say that every successful patch will get an account banned. I also would not promise that a patched client is safe for an account someone depends on.

For a tool whose purpose is to make daily work easier, removing security boundaries and replacing credential storage is a poor default. Those are not tradeoffs I want to hide inside a launcher feature.

What I would use for two accounts

The investigation leaves three quite different approaches to WhatsApp on a Mac. There is the official native client, a separate web session, and a modified native copy with changes that need their own maintenance and security review. A separate window alone tells you little about account isolation or credential protection.

Parall uses the web approach for WhatsApp and says so. Its separate WebKit sessions are useful for keeping accounts apart. I am not going to describe them as extra instances of Meta's native WhatsApp client.

For everyday use, one official Mac client plus a separate WhatsApp Web session is a straightforward option. Separate web sessions can also serve multiple accounts, including through Parall.

An Android virtual machine is another direction to investigate if you want to experiment with the Android client. That would be a different environment running a different app. I have not validated that setup here, and I would not promise that registration, linking, or notifications will work reliably in a particular VM. It does not give you two native Mac clients.

Where I would stop searching

The reasonable wish that started this investigation was simple. Two accounts, two native WhatsApp instances, one Mac.

WhatsApp's own code gives us a much more specific explanation for why an ordinary launcher cannot deliver that. Its storage depends on sandbox and app group identities, its original executable has runtime protections, and its own code checks for another running instance and asks the new one to quit.

Changing the app's identity and intercepting its storage APIs offer ways around individual obstacles. That is why I would not claim that native duplication is universally impossible. But an approach that removes the sandbox, disables hardened runtime, and weakens credential storage is a poor tradeoff for accounts I depend on.

The investigation did not uncover a simple launch setting for independent accounts. It led to changes in identity, storage, and security. Those changes deserve scrutiny before a second native window becomes part of someone's daily messaging setup.

Meta could provide a supported desktop account switcher or independent profiles. Until then, I prefer a clearly explained web session, including the approach used by Parall, to a modified native client with weaker protections. An extra native window is not worth that tradeoff to me.

You should know what you are opening before you trust it with your conversations.

Comments