ruvnet/ruflo · error

module loaded but is missing expected OAuth exports

Error message

module loaded but is missing expected OAuth exports

What it means

loadSecurityOAuth() dynamically imports @claude-flow/security and probes for authorizeUrl/createKeychainAdapter. If the module resolves but those exports are absent, the install is broken or the resolved version's API doesn't match what the CLI expects (version skew, partial install, a same-named package shadowing it). Note the probe throw happens inside the try, so callers actually receive SecurityPackageMissingError whose underlying message is this string.

Solutions

  1. Align versions: install the @claude-flow/security version the CLI declares (check @claude-flow/cli's package.json optionalDependencies), e.g. npm install @claude-flow/security@<matching-version>
  2. Clean reinstall: rm -rf node_modules package-lock.json && npm install
  3. Verify what actually resolves: node -e "import('@claude-flow/security').then(m => console.log(Object.keys(m)))" — confirm authorizeUrl and createKeychainAdapter are present
  4. Check for duplicate installs / path shadowing: npm ls @claude-flow/security

Example fix

# before
npm install @claude-flow/cli @claude-flow/security@0.9.0   # mismatched versions
# after
npm install @claude-flow/cli && npm install @claude-flow/security@$(node -p "require('@claude-flow/cli/package.json').optionalDependencies['@claude-flow/security']")
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: confirm the installed security package exposes the OAuth surface
const mod = await import('@claude-flow/security');
if (typeof mod.authorizeUrl !== 'function' || typeof mod.createKeychainAdapter !== 'function') {
  throw new Error('incompatible @claude-flow/security version — align it with @claude-flow/cli');
}

Type guard

import { SecurityPackageMissingError } from '@claude-flow/cli/dist/auth/security-bridge.js';
function isSecurityPackageProblem(e: unknown): e is SecurityPackageMissingError {
  return e instanceof Error && e.name === 'SecurityPackageMissingError';
}

Try / catch

try {
  await runAuthCommand();
} catch (e) {
  if (isSecurityPackageProblem(e) && e.message.includes('missing expected OAuth exports')) {
    console.error('Version mismatch: install the @claude-flow/security version declared by @claude-flow/cli');
    process.exit(5);
  }
  throw e;
}

Prevention

When it happens

Trigger: An @claude-flow/security version older/newer than the CLI's expected OAuth surface is installed (mixed-version monorepo or pinned old version); node_modules partially written by an interrupted install; a bundler/test mock resolving in place of the real package; a rogue package with the same name earlier on the resolution path.

Common situations: npm workspaces/monorepos where a different package pinned an incompatible @claude-flow/security version; `npm install --force` or interrupted installs leaving stub modules; test suites that mock '@claude-flow/security' incompletely (missing authorizeUrl/createKeychainAdapter).

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/c094f2101b2476fd. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/auth/security-bridge.ts:70

      "ruflo auth needs the '@claude-flow/security' package, which isn't installed " +
        "(it's an optional dependency — install/reinstall failed or was skipped for this " +
        `platform). Try: npm install @claude-flow/security. Underlying error: ${
          cause instanceof Error ? cause.message : String(cause)
        }`,
    );
    this.name = 'SecurityPackageMissingError';
  }
}

let cached: SecurityOAuthModule | null = null;

/** Loads `@claude-flow/security`'s OAuth surface, throwing a clear error if it's absent. */
export async function loadSecurityOAuth(): Promise<SecurityOAuthModule> {
  if (cached) return cached;
  try {
    const mod = (await import('@claude-flow/security')) as unknown as SecurityOAuthModule;
    if (!mod.authorizeUrl || !mod.createKeychainAdapter) {
      throw new Error('module loaded but is missing expected OAuth exports');
    }
    cached = mod;
    return mod;
  } catch (e) {
    throw new SecurityPackageMissingError(e);
  }
}

View on GitHub (pinned to fa13ee4ad6)