# Composer Says Packages "Were Not Loaded Because They Are Affected by Security Advisories": What to Do

> Since Composer 2.9, composer update refuses to install package versions with known security advisories. Here is what the error means, how to fix it properly, and how to ignore a specific advisory with a recorded reason when you genuinely are not affected.

**Author:** Matt King | **Published:** September 30, 2026 | **Category:** Security

---

You run `composer update` and it fails with something like this:

```text
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires acme/pdf-tools ^3.1, found acme/pdf-tools[3.1.0, ..., 3.4.2]
      but these were not loaded, because they are affected by security advisories.
```

Nothing is broken. Composer is refusing to install a package version with a known security vulnerability, and every version your constraints allow is affected. This post covers why it happens, how to fix it properly, and how to ignore one specific advisory safely when you genuinely are not affected.

---

## What changed in Composer

[Composer 2.9.0](https://github.com/composer/composer/blob/main/CHANGELOG.md), released in November 2025, added automatic blocking of packages with security advisories. When Composer resolves dependencies, it now skips any version that has a published advisory, using the same advisory data as `composer audit`.

Composer 2.10.0 (May 2026) moved the settings into a new `policy` config block. The older `audit.*` settings still work but are deprecated. Check which version you have before copying config from anywhere, including this post:

```bash
composer --version
composer self-update
```

### When blocking applies

Blocking happens during dependency **resolution**: `composer update`, `composer require` and `composer remove`. Installing from an existing `composer.lock` with `composer install` does not block on advisories.

That explains a confusing situation: production deploys keep working, CI installs keep working, and then someone runs `composer update` locally and it fails. The vulnerable version was already in your lock file. Composer has just stopped letting you choose it again.

To see what in your lock file is affected right now:

```bash
composer audit --locked
```

For a full walkthrough of reading and acting on `composer audit` output, see [Composer Audit: Find Vulnerable PHP Packages](/blog/composer-vulnerability-management).

---

## Fix it properly first

In most cases the advisory has a patched release and your constraint is just too narrow. Work through these in order.

**1. Find the advisory and the fixed version.**

```bash
composer audit --format=plain
```

Each advisory lists the affected version range and a link (usually a GitHub Security Advisory, `GHSA-...`, or a CVE). The fixed version is the first one outside the affected range.

**2. Widen your constraint to allow the fixed version.** If the fix is in `3.5.0` and you require `^3.1` with an upper bound somewhere else, adjust it:

```bash
composer require acme/pdf-tools:^3.5 --update-with-dependencies
```

**3. If the package is a transitive dependency**, find what is holding it back:

```bash
composer why acme/pdf-tools
composer why-not acme/pdf-tools 3.5.0
```

`why-not` tells you which of your direct dependencies refuses the fixed version. Updating that dependency usually unblocks it.

**4. If there is no fixed release yet**, you have a real decision to make: wait, replace the package, or accept the risk for now. Only the last one involves ignoring the advisory.

---

## Ignoring one advisory, with a reason

Sometimes an advisory genuinely does not apply. The vulnerable function is in a code path you never call, or the issue needs a configuration you do not use. In that case, ignore that **one advisory ID**, and write down why.

### Composer 2.10 and later

```json
{
    "config": {
        "policy": {
            "advisories": {
                "ignore-id": {
                    "GHSA-xxxx-xxxx-xxxx": "Only affects the HTML renderer, which we never use. Review 2026-12-01."
                }
            }
        }
    }
}
```

`ignore-id` also accepts a detailed form if you want the advisory ignored for blocking but still reported by `composer audit`, which is a sensible default:

```json
"ignore-id": {
    "GHSA-xxxx-xxxx-xxxx": {
        "on-audit": false,
        "reason": "Only affects the HTML renderer, which we never use. Review 2026-12-01."
    }
}
```

### Composer 2.9

The equivalent setting is `audit.ignore`, which takes the same ID-to-reason map:

```json
{
    "config": {
        "audit": {
            "ignore": {
                "GHSA-xxxx-xxxx-xxxx": "Only affects the HTML renderer, which we never use. Review 2026-12-01."
            }
        }
    }
}
```

From 2.9.2, each entry can also take the detailed form `{"apply": "block", "reason": "..."}` so it is only ignored for blocking.

### What not to ignore

Both config styles also let you ignore whole packages or entire severity levels (`ignore` and `ignore-severity` under `policy.advisories`, or `audit.ignore-severity` on 2.9). Avoid both. Ignoring a package or a severity silences every advisory published against it from now on, including the serious one next month that you would actually care about. An ignore entry should be as narrow as the reason you wrote for it.

Put a review date in the reason, and treat an ignored advisory as a ticket rather than a decision. It is easy to add an ignore during an incident and leave it there for two years.

---

## Bypassing blocking for one command

When you need to get unstuck right now, for example to produce a lock file while you investigate, disable blocking for a single command:

```bash
composer update --no-blocking
```

Or with an environment variable:

```bash
COMPOSER_NO_BLOCKING=1 composer update
```

On Composer 2.9, the flag was `--no-security-blocking` and the variable `COMPOSER_NO_SECURITY_BLOCKING=1`. Both still work on 2.10 but are deprecated.

Two things to know:

- `--no-audit` does **not** disable blocking. It only skips the audit summary Composer prints after an install or update.
- Do not put `--no-blocking` in CI scripts or deploy commands. That turns a one-off workaround into a permanent hole.

### Turning it off entirely

You can disable all policy enforcement by setting `policy` to `false` (2.10+) or `audit.block-insecure` to `false` (2.9). Don't. It removes the protection for every package you will ever add, and the next vulnerable release will install silently.

---

## Keep the check in CI as well

Blocking only protects you when someone runs an update. Your existing lock file can become vulnerable at any time, because advisories are published after you install. Run the audit on every build:

```yaml
- run: composer install --no-interaction --prefer-dist
- run: composer audit --locked
```

`composer audit` exits non-zero when it finds advisories, so the build fails until someone fixes or explicitly ignores them. Since Composer 2.10 the exit code is simply 0 or 1.

[roave/security-advisories](https://github.com/Roave/SecurityAdvisories) is an older approach to the same problem: a metapackage that conflicts with every known-vulnerable version. With Composer 2.9's built-in blocking it is largely redundant for resolution, but `composer audit` in CI is still the piece most teams are missing.

---

## The part Composer cannot see

Composer knows what is in your lock file. It does not know whether the vulnerable code is reachable on your deployed application, whether you deployed the patched lock file to every server, or whether an old release is still running behind a load balancer.

That is the outside view. StackShield checks the deployed Laravel application from the internet, including exposed debug tools and files that dependency audits never look at. [Run a free scan](/free-scan) to see what your production app exposes, and read about [PHP supply chain attacks through Composer](/blog/php-supply-chain-attacks-composer) for the threats advisory data does not cover.

---

## Frequently Asked Questions

### Why does Composer say packages were not loaded because they are affected by security advisories?

Since Composer 2.9.0 (November 2025), dependency resolution skips any package version that has a known security advisory. If the only versions matching your constraints are affected, resolution fails with this message. It is a safety feature, not a bug: Composer is refusing to install something known to be vulnerable.

### Does Composer block composer install from an existing lock file?

No. Advisory blocking applies when Composer resolves dependencies: composer update, require and remove. Installing from an existing composer.lock does not re-check advisories for blocking, which is why a deploy can keep working while a teammate cannot update. Run composer audit to see the advisories that affect your lock file.

### How do I ignore a specific security advisory in Composer?

On Composer 2.10 or later, add the advisory ID to config.policy.advisories.ignore-id in composer.json, ideally as a map of ID to reason. On Composer 2.9, use config.audit.ignore, which accepts the same ID-to-reason map. Ignore single advisory IDs, never whole packages or severities, and record why you are not affected.

### How do I temporarily bypass Composer security blocking?

Pass --no-blocking to update, require, remove or install, or set COMPOSER_NO_BLOCKING=1. Older 2.9 releases used --no-security-blocking and COMPOSER_NO_SECURITY_BLOCKING, which still work but are deprecated. Note that --no-audit does not disable blocking; it only skips the audit report after install.

### Should I turn advisory blocking off entirely?

No. Setting policy (or audit.block-insecure on 2.9) to false removes the protection for every package, including advisories published next month. Fix the dependency, or ignore one specific advisory with a reason and a review date. That keeps the safety net in place for everything else.

