Introduction

CodeQL analysis can be much heavier than a normal unit test workflow. Large monorepos, generated code, and multi-language scans can push a standard runner past its memory limit, which causes the job to die mid-analysis. The fix is usually a combination of reducing unnecessary scan scope and giving the analysis enough runner resources instead of assuming the default runner size will always be enough.

Symptoms

  • CodeQL analysis terminates with out-of-memory messages
  • The runner disconnects or the analysis step is cancelled abruptly
  • Only larger branches or full-repository scans fail while smaller changes pass
  • Multi-language CodeQL runs are much less stable than single-language runs

Common Causes

  • The repository is too large for the default runner memory budget
  • CodeQL scans generated code, test fixtures, or directories that add little security value
  • Multiple languages are analyzed in one heavy job
  • The workflow runs full CodeQL analysis on every trigger instead of using a narrower scope

Step-by-Step Fix

  1. 1.Reduce the analysis scope first
  2. 2.Excluding generated output, vendored dependencies, and irrelevant large directories often removes the memory spike faster than adding raw runner capacity.
yaml
- uses: github/codeql-action/init@v3
  with:
    languages: javascript
    config-file: ./.github/codeql/codeql-config.yml
  1. 1.Split languages into separate jobs if needed
  2. 2.A combined multi-language scan can exceed memory even when each language succeeds on its own.
  3. 3.Move heavy CodeQL runs onto a larger runner
  4. 4.If the repository genuinely needs broad analysis, use a runner class that matches the workload rather than squeezing it into the default memory envelope.
  5. 5.Run the heaviest scans on a schedule if pull request cadence is too expensive
  6. 6.Security coverage is still useful even if the broadest scan runs nightly instead of on every PR.

Prevention

  • Exclude generated code and irrelevant directories from CodeQL by default
  • Keep language scans separate when repository size justifies it
  • Match runner size to the real CodeQL workload, not to the unit test workload
  • Monitor memory-related CI failures after significant repository growth