Error: Cannot find module in GitHub Actions: Complete Troubleshooting Guide

Dependency

Quick Answer

If a GitHub Actions workflow reports “Error: Cannot find module ‘module-name'”, it means Node.js cannot locate the specified package or file during the CI/CD workflow execution.

Most common causes:

  • Missing npm install or npm ci step in workflow
  • node_modules not cached or cache restoration failed
  • Private npm packages without authentication token
  • Path case sensitivity issues (Windows local → Linux runner)
  • Lock file not committed or out of sync with package.json

Immediate diagnostic steps:

- name: Debug module resolution
  run: |
    npm ls <package-name>          # Verify package is installed
    ls -la node_modules | head -20 # Check node_modules contents
    node --trace-resolution index.js  # Debug module resolution path

What this error means

According to the Node.js official module resolution documentation, Node.js uses a specific algorithm to locate files when you use require() or import. When this algorithm fails during a GitHub Actions workflow, it throws an error that can be harder to debug than in local development.

Error Message Variants in CI/CD

Depending on your workflow configuration and module system, you may see one of these error formats:

Standard CommonJS Error:

Error: Cannot find module 'express'
Require stack:
- /home/runner/work/my-project/my-project/src/app.js
- /home/runner/work/my-project/my-project/test/app.test.js

ES Modules Error:

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'lodash' imported from /home/runner/work/my-project/my-project/src/utils.js

GitHub Actions Context Error:

Run npm test
  npm test
  shell: /usr/bin/bash -e {0}
Error: Cannot find module '@company/private-package'
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader.js:xxx)

TypeScript Compilation Error:

Run npx ts-node src/index.ts
Error: Cannot find module '@app/utils'
Require stack:
- /home/runner/work/my-project/my-project/src/index.ts

These errors indicate that despite your workflow configuration, Node’s resolver cannot find the requested module in the GitHub Actions runner environment.

Why this happens

Root Causes Overview

Cause Frequency Complexity
Missing install step Very High Low
Cache misconfiguration High Medium
Private package auth failure High Medium
Case sensitivity issues Medium Medium
Lock file out of sync Medium Low
Monorepo workspace issues Medium High
Build artifacts missing Low Medium
Architecture mismatch Low High

The GitHub Actions Environment

GitHub Actions runners are ephemeral Ubuntu (Linux) environments that start fresh for each workflow run. Key characteristics:

  • Fresh environment – No node_modules from previous runs (unless cached)
  • Linux filesystem – Case-sensitive, unlike Windows/macOS
  • No global packages – Unlike your local development machine
  • Isolated network – Requires explicit authentication for private resources

Common scenarios

Scenario A: Missing Install Step

Your workflow fails immediately when trying to run tests or build.

# ❌ Wrong - Missing install step
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test  # Error: Cannot find module

Framework-specific examples:

  • Express.js API: Error: Cannot find module 'express'
  • React/Vue/Angular: Error: Cannot find module 'react'
  • Next.js: Error: Cannot find module 'next'
  • Jest Testing: Error: Cannot find module 'jest'
  • TypeScript: Error: Cannot find module 'typescript'

Scenario B: Cache Restoration Failure

Your workflow worked before but suddenly fails, or cache hits but modules are still missing.

- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'

- run: npm ci
- run: npm test  # Error: Cannot find module

Why it happens:

  • Lock file changed but cache key didn’t update
  • Cache was corrupted or partially restored
  • node_modules partially exists but is incomplete

Scenario C: Private Package Authentication Failure

Error: Cannot find module '@mycompany/private-package'
npm ERR! 404 Not Found - GET https://registry.npmjs.org/@mycompany%2fprivate-package

Why it happens:

  • NPM_TOKEN not set in repository secrets
  • .npmrc file not configured for CI
  • GitHub Packages authentication missing

Common private package scenarios:

  • @company/shared-utils – Internal utility packages
  • @org/private-sdk – Organization SDKs
  • GitHub Packages hosted packages
  • Artifactory/Nexus private registries

Scenario D: Case Sensitivity Hell

Your code works perfectly on your Windows or macOS machine but fails in GitHub Actions.

// File: src/Components/Button.js
const Button = require('./components/button'); // Wrong case

Why it happens: According to Node.js documentation, module resolution is case-sensitive on Linux (GitHub Actions runners) but case-insensitive on Windows and macOS (by default).

Scenario E: Lock File Out of Sync

npm ERR! `npm ci` can only install packages when your package.json and package-lock.json are in sync.

Why it happens:

  • package-lock.json not committed to git
  • npm install used locally instead of npm ci
  • package.json modified without updating lock file

Scenario F: Monorepo / Workspace Issues

Workspace packages can’t find each other in GitHub Actions.

// In @myorg/package-b
import { helper } from '@myorg/package-a';
// Error: Cannot find module '@myorg/package-a'

Common monorepo tools affected:

  • npm Workspaces
  • Yarn Workspaces
  • pnpm Workspaces
  • Turborepo
  • Nx
  • Lerna

Scenario G: Build Artifacts Missing

Error: Cannot find module '/home/runner/work/my-project/my-project/dist/index.js'

Why it happens:

  • TypeScript not compiled before running tests
  • Build step skipped or failed silently
  • Output directory not preserved between jobs

Scenario H: Job Artifacts Not Passed

Install step succeeds in Job A, but Job B fails with “Cannot find module”.

Why it happens:

  • Each job runs on a fresh runner instance
  • node_modules not uploaded as artifact
  • Jobs don’t share the filesystem

How to fix

Fix A: Add Proper Install Step

Symptoms: Missing node_modules in workflow run.

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test

Key points:

  • Always use actions/setup-node before installing
  • Use npm ci instead of npm install for reproducible builds
  • Place install step before any step that needs dependencies

Fix B: Configure Caching Correctly

Symptoms: Slow installs or cache-related failures.

- name: Setup Node.js with caching
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'

- name: Install dependencies
  run: npm ci

For custom cache configuration:

- name: Cache node_modules
  uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

- name: Install dependencies
  run: npm ci

Fix C: Configure Private Package Authentication

Symptoms: 404 errors for private packages.

For npm private packages:

- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    registry-url: 'https://registry.npmjs.org'

- name: Install dependencies
  run: npm ci
  env:
    NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Create .npmrc in repository:

//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}

For GitHub Packages:

- name: Setup Node.js for GitHub Packages
  uses: actions/setup-node@v4
  with:
    node-version: '20'
    registry-url: 'https://npm.pkg.github.com'

- name: Install dependencies
  run: npm ci
  env:
    NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Fix D: Fix Case Sensitivity Issues

Symptoms: Works locally on Windows/macOS, fails in GitHub Actions.

# Find case mismatches before committing
git ls-files | sort -f | uniq -di

Always use exact casing:

// File is named UserProfile.js
const UserProfile = require('./UserProfile'); // ✅ Correct
const UserProfile = require('./userprofile'); // ❌ Wrong
const UserProfile = require('./userProfile'); // ❌ Wrong

Enable strict case checking in TypeScript:

{
  "compilerOptions": {
    "forceConsistentCasingInFileNames": true
  }
}

Fix E: Ensure Lock File is Committed and Synced

Symptoms: npm ci fails with lock file errors.

# Locally - ensure lock file is up to date
rm -rf node_modules package-lock.json
npm install

# Commit the updated lock file
git add package-lock.json
git commit -m "chore: update lock file"
git push

In workflow:

- name: Install dependencies
  run: npm ci  # Requires lock file to be in sync

Fix F: Configure Monorepo Workspaces

Symptoms: Workspace packages can’t find each other.

For npm workspaces:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci  # Installs all workspace dependencies
      
      - name: Build packages
        run: npm run build --workspaces
      
      - name: Run tests
        run: npm test --workspaces

For pnpm workspaces:

- name: Setup pnpm
  uses: pnpm/action-setup@v2
  with:
    version: 8

- name: Install dependencies
  run: pnpm install --frozen-lockfile

- name: Build
  run: pnpm build

Fix G: Ensure Build Artifacts Exist

Symptoms: Cannot find compiled files or dist directory.

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build TypeScript
        run: npm run build
      
      - name: Run tests
        run: npm test

Upload build artifacts for later jobs:

- name: Build
  run: npm run build

- name: Upload build artifacts
  uses: actions/upload-artifact@v4
  with:
    name: dist
    path: dist/

Fix H: Share Dependencies Between Jobs

Symptoms: Job B fails even though Job A installed dependencies.

jobs:
  install:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Upload node_modules
        uses: actions/upload-artifact@v4
        with:
          name: node_modules
          path: node_modules/

  test:
    needs: install
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Download node_modules
        uses: actions/download-artifact@v4
        with:
          name: node_modules
          path: node_modules/
      
      - name: Run tests
        run: npm test

Checklist

Use this checklist to systematically resolve module errors in GitHub Actions:

  • Add actions/setup-node step before installing
  • Use npm ci instead of npm install
  • Verify package-lock.json is committed to git
  • Configure caching with cache: 'npm'
  • Add install step before test/build steps
  • Check case sensitivity of import paths
  • Configure NODE_AUTH_TOKEN for private packages
  • Ensure build step runs before tests (TypeScript)
  • Upload/download artifacts between dependent jobs
  • Clear cache if experiencing weird behavior

Quick diagnostic workflow:

- name: Debug module resolution
  run: |
    echo "Node version: $(node --version)"
    echo "NPM version: $(npm --version)"
    echo "---"
    echo "Package.json dependencies:"
    cat package.json | grep -A 20 '"dependencies"'
    echo "---"
    echo "Installed packages:"
    npm ls --depth=0 || true
    echo "---"
    echo "node_modules contents:"
    ls -la node_modules | head -20

FAQ

Why does my code work locally but fail in GitHub Actions?

A: Most common reasons:

  • node_modules exists locally but isn’t in git (GitHub Actions starts fresh)
  • Different operating systems (Windows/macOS local → Linux runner)
  • Local global packages not available in CI
  • .env files or local configuration not committed
  • Case sensitivity differences

How do I fix “Cannot find module” for private npm packages?

A:

  1. Add NPM_TOKEN to repository secrets (Settings > Secrets > Actions)
  2. Configure actions/setup-node with registry URL
  3. Pass token via NODE_AUTH_TOKEN environment variable
- uses: actions/setup-node@v4
  with:
    node-version: '20'
    registry-url: 'https://registry.npmjs.org'
- run: npm ci
  env:
    NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Should I use npm install or npm ci in GitHub Actions?

A: Always use npm ci in CI/CD environments because:

  • It’s faster (skips certain user-oriented features)
  • It strictly follows package-lock.json for reproducible builds
  • It fails if package.json and lock file are out of sync
  • It deletes existing node_modules for a clean install

Only use npm install if you don’t have a lock file (not recommended).

How do I cache node_modules in GitHub Actions?

A: Use actions/setup-node with built-in caching:

- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'  # Automatically caches ~/.npm

Or use actions/cache for more control:

- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

Why is npm ci failing with lock file errors?

A: npm ci requires package-lock.json to be in sync with package.json. Fix:

# Locally
rm -rf node_modules package-lock.json
npm install

# Commit the updated lock file
git add package-lock.json
git commit -m "fix: update lock file"
git push

How do I handle monorepos in GitHub Actions?

A: For npm workspaces:

- run: npm ci  # Installs all workspace dependencies
- run: npm run build --workspaces
- run: npm test --workspaces

For Turborepo with remote caching:

- uses: actions/setup-node@v4
- uses: dtinth/setup-github-actions-caching-for-turbo@v1
- run: npm ci
- run: npx turbo run build test

Can I use different Node.js versions in different jobs?

A: Yes, specify version per job:

jobs:
  test-node-18:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: '18'
      - run: npm ci
      - run: npm test

  test-node-20:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm test

How do I debug “Cannot find module” in GitHub Actions?

A: Add debug steps to your workflow:

- name: Debug dependencies
  run: |
    echo "=== Node Version ==="
    node --version
    echo "=== NPM Version ==="
    npm --version
    echo "=== Package.json ==="
    cat package.json
    echo "=== Installed Packages ==="
    npm ls || true
    echo "=== node_modules ==="
    ls -la node_modules/ | head -30
    echo "=== Trace Resolution ==="
    node --trace-resolution index.js || true

Why are my workspace packages not found in GitHub Actions?

A: Common causes:

  • Missing build step for workspace packages
  • Incorrect workspace configuration
  • Dependencies not hoisted properly

Fix:

- run: npm ci
- run: npm run build --workspaces  # Build all packages first
- run: npm test --workspaces

How do I handle native modules (node-gyp) in GitHub Actions?

A: Install build tools before npm install:

- name: Install build tools
  run: |
    sudo apt-get update
    sudo apt-get install -y python3 make g++

- name: Install dependencies
  run: npm ci

Or use prebuilt binaries when available:

npm install sharp --platform=linux --arch=x64

Related errors

滚动至顶部