Skip to content

[MiniMax Code Desktop] Startup conflict check misidentifies the daemon's own symlink as a global npm install #38

@VerbalChainsaw

Description

@VerbalChainsaw

Summary

The MiniMax Code desktop app's startup pre-flight check misidentifies the daemon's own CLI shim at <dataDir>/bin/mavis.cmd (a 172-byte Windows batch file the daemon writes on first boot to expose the mavis command via the bundled cli.js) for a global npm install of @minimax/mavis. The check produces a false-positive conflict dialog and a self-recovery loop that the user cannot break without manual intervention.

Environment

  • MiniMax Code version 3.0.42 (daemon mtime 2026-06-09, version 3.0.42 per daemon/package.json)
  • Windows 10
  • Node 22.22.3, npm 10.9.8
  • No global npm install of @minimax/mavis (npm list -g @minimax/mavis returns empty; npm root -g is %AppData%\Roaming\npm\node_modules which contains no @minimax directory)
  • Reproducible after the vendor's silent auto-update has run at least once (the trigger condition: the user-state bin/mavis.cmd exists from a prior boot AND the auto-updater has stamped the install)

Steps to reproduce

  1. Install MiniMax Code via the official installer.
  2. Launch MiniMax Code once. Verify the daemon creates the CLI shim at <dataDir>/bin/mavis.cmd (the daemon's first-boot does this — see daemon.js:349371-349384 in the bundle, function ensureMavisCli()).
  3. Wait for the vendor's auto-updater to fire. The default update interval is approximately 30 minutes after first launch (observed in the wild, but not documented in any public spec; the actual interval is per the Updater config embedded in the app).
  4. The next user-initiated or auto-triggered relaunch hits the "Installation Conflict Detected" dialog.

Observed behavior

The app shows:

Installation Conflict Detected
The npm package @minimax/mavis is already installed (<dataDir>\bin\mavis). It conflicts with the desktop app. Please uninstall it first, then restart the app:

npm uninstall -g @minimax/mavis

The app then enters a "recovery" loop that:

  1. Calls npm uninstall -g @minimax/mavis (returns up to date in Nms — no-op, because nothing is globally installed)
  2. Removes the scheduled tasks MavisDaemon and AgentArchonDaemon
  3. Relaunches
  4. Hits the same conflict check
  5. Repeats indefinitely (every ~5 seconds in the captured log)

The daemon never starts. Port 15321 is never bound. The MiniMax Code window never appears.

Expected behavior

The conflict check should return a negative result because no global install exists. The recovery loop should detect that the npm uninstall was a no-op and stop, rather than relaunching into the same false-positive.

Evidence

archon-06-09.log lines from one observed failure (timestamps from the user's local log; path redacted to <dataDir>):

[06:39:06.990] [info] [Daemon] mode=prod, NODE_ENV=undefined
[06:39:07.599] [info] Data directory already migrated (.mavis → .minimax)
[06:39:07.602] [error] [Daemon] npm package @minimax/mavis conflict detected: <dataDir>\bin\mavis
[06:39:24.514] [info] [KillNpmDaemon] Removed scheduled task: MavisDaemon
[06:39:24.547] [info] [KillNpmDaemon] Removed scheduled task: AgentArchonDaemon
[06:39:31.731] [info] [UninstallNpm] up to date in 6s
[06:39:32.969] [error] [Daemon] npm package @minimax/mavis conflict detected: <dataDir>\bin\mavis
[06:39:51.379] [info] [KillNpmDaemon] Removed scheduled task: MavisDaemon
[06:39:51.422] [info] [KillNpmDaemon] Removed scheduled task: AgentArchonDaemon
[06:39:51.915] [info] [UninstallNpm] up to date in 283ms
[06:39:52.XXX] [error] [Daemon] npm package @minimax/mavis conflict detected: <dataDir>\bin\mavis

The conflict error and the up to date no-op alternate ~5 seconds apart, confirming the self-recovery loop. Total observed run: 16 conflict detections in a 6-minute window before the user intervened.

What's known about the conflicting file

<dataDir>/bin/mavis.cmd is a 172-byte Windows batch file the daemon's first-boot writes itself. The actual content (from the user's install):

@echo off
chcp 65001 > nul
set ELECTRON_RUN_AS_NODE=1
"<install-path>\MiniMax Code.exe" "<install-path>\resources\resources\daemon\cli.js" %*

It is NOT a global npm install. It is a self-installed shim that invokes the bundled cli.js via the Electron binary. The same logic is in the daemon bundle at daemon.js:349371-349384:

const localBinDir = path.join(_config.dataDir, "bin");
mkdirSync(localBinDir, { recursive: true });
const isWin = process.platform === "win32";
const localSymlink = path.join(localBinDir, isWin ? "mavis.cmd" : "mavis");
if (isWin) {
  const wrapperContent = `@echo off\r\r\nchcp 65001 > nul\r\r\nnode "${cliEntry}" %*\r\r\n`;
  let needsWrite = true;
  try {
    if (existsSync(localSymlink) && readFileSync(localSymlink, "utf-8") === wrapperContent) {
      needsWrite = false;
    }
  } catch {}
  if (needsWrite) writeFileSync(localSymlink, wrapperContent);
} else {
  // POSIX: real symlink
}

(Note: the bundled wrapperContent template invokes node "${cliEntry}", but the version on the user's install invokes the Electron binary as Node via ELECTRON_RUN_AS_NODE=1. The exact content differs from the bundled template, but the intent — exposing mavis as a CLI shim — is the same.)

Where the conflict check is NOT (helpful for triage)

The strings npm package @minimax/mavis conflict detected, KillNpmDaemon, UninstallNpm, and MavisDaemon do NOT appear in the current daemon.js bundle (verified by ripgrep against daemon.js version 3.0.42). The conflict-detection code may live in:

  • app.asar (the Electron renderer, 143MB, not currently extractable without npx @electron/asar)
  • A different module loaded at runtime
  • A newer daemon build installed by the silent auto-updater

If the maintainer can identify the actual location of the conflict check, that would unblock the fix. The log evidence above should make that straightforward.

Suggested fix (vendor-side)

Replace the filesystem-presence check at the conflict-detection call site with the canonical npm list -g @minimax/mavis check, which returns the right answer (no global install) and does not false-positive on the daemon's own self-installed shim. The daemon bundle already has NPM_REGISTRY = "https://npmmirror.xaminim.com/" and NPM_PACKAGE = "@minimax/mavis" (see daemon.js:216139-216140) and a fetchLatestNpmVersion() function (L216185+) that consults the registry — the same call pattern would correctly identify the absent global install.

Additionally, the recovery loop should detect the up to date in Nms no-op response from npm uninstall and exit instead of relaunching into the same false-positive.

Workaround (until the vendor fixes it)

The user can rename the bin/ directory so the conflict check sees a missing path, then re-launch. After a successful boot, the daemon re-creates bin/ correctly. Note: the daemon's first-boot logic may re-seed other user-state files with defaults; users who customize the agent's opencode config should re-apply their changes after the relaunch.

Get-Process -Name 'MiniMax Code' -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 2
if (Test-Path "$env:USERPROFILE\.minimax\bin") {
    Rename-Item "$env:USERPROFILE\.minimax\bin" "$env:USERPROFILE\.minimax\bin-disabled-by-fix"
}
Start-Process -FilePath (Get-Command 'MiniMax Code.exe').Source
Start-Sleep -Seconds 15
Get-NetTCPConnection -LocalPort 15321 -State Listen

Severity

P3 (medium). The app is blocked from starting, but the user can recover with a documented workaround that does not touch the bundle. A P0/P1 severity would require that the workaround be impossible, undocumented, or destructive.

Related prior issues (same vendor app-lifecycle family)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions