Skip to content

Troubleshooting

Parts of Laravel remain genuinely too dynamic to analyse. This page collects the known limits and what to do about each. If your problem is a property or relation that is not resolving, start with the FAQ instead.

Ignoring what cannot be fixed

Where a limit is real, ignore it by identifier rather than by message or by line. Identifiers are covered by the backward compatibility policy and message wording is not, so a message-based ignore can silently stop matching in any release and start failing your build.

parameters:
    ignoreErrors:
        - identifier: laravel.modelMake

Leave reportUnmatchedIgnoredErrors at its default of true, so that an ignore which no longer matches is reported rather than left to rot. The cost is that a release which fixes a false positive fails your build until you delete the now-unused entry, which is the right trade in most projects. Backward compatibility covers when to turn it off.

Higher order messages on a support collection

The higher order proxy is understood, but only as far as the collection's value type is known. On an Eloquent\Collection that is a model and everything resolves. On a plain Support\Collection whose values are mixed, the proxy has nothing to look the method up on:

parameters:
    ignoreErrors:
        - '#Call to an undefined method Illuminate\\Support\\HigherOrder#'

The better fix is to give the collection a value type, which resolves it properly rather than hiding it:

/** @var Collection<int, User> $users */
$users->groupBy->email; // Collection<string, Collection<int, User>>

Collection value types are invariant

Collection declares TValue invariantly, so a collection of a narrower value type is not accepted where a wider one is annotated, even though reading from it would be perfectly safe:

/** @param Collection<int, string|null> $values */
public function accept(Collection $values): void {}

The error is easy to misread, because PHPStan prints the widened description on both sides and the two types look identical. The Tip about TValue not being covariant is the only part that tells you what happened. If you find yourself suspecting a broken return type extension, check for that line first.

The fix is use-site variance, not an ignore:

/** @param Collection<int, covariant string|null> $values */

covariant on a parameter says you only read from the collection. Prefer it to suppressing argument.type, which would hide real mismatches in the same signature. For keys, array-key does the same job, being a benevolent union; a plain int|string does not, since it is matched invariantly.

This is a genuine limit rather than a missing feature. Collection::push(), put(), prepend(), add() and offsetSet() all take a TValue, so the collection consumes its own value type and cannot be covariant in it. The framework annotates it as covariant anyway, but nothing enforces that annotation against the framework's own source—see the FAQ for why the stubs cannot repeat it.

Macros

Macros registered while the application boots are discovered automatically. A registration that does not run during analysis needs an @method annotation. The macros guide covers discovery, static and instance macros, and classes whose macros have a static-facing API.

Models without resolvable columns

A model whose table this extension cannot see, because the table belongs to another service or is created outside migrations, will have every column reported as missing once modelPropertyType is on. Annotate those models with @property and they behave normally.

Analysis is quiet on a package

If no application is found and Testbench is not installed, analysis runs but every container lookup returns nothing, so config types, app() resolution and view names all fall back to their widest types. Nothing errors, which makes it look like the extension is doing very little. See analysing a package.

The run fails while booting

Booting is fatal by design: nothing useful can be analysed once a service provider has failed, so the underlying exception is reported rather than a wall of unrelated errors. The message points at the real failure, which is usually a missing environment variable or a provider that expects a service the analysis environment does not have. Booting your application covers what runs and what to keep out of it.

A schema dump cannot be parsed

An unreadable dump fails the run rather than being skipped, because the tables it defines would otherwise go missing from your model properties with nothing to say so. Options, in order of preference: fix the dump, choose the other parser with sqlParser, or set scanSchema: false to opt out of dumps entirely.

The iamcal and phpmyadmin drivers parse MySQL dumps. PostgreSQL plain-text dumps require calebdw/pg-schema-parser; use sqlParser: postgres when a MySQL parser is also installed. Custom and directory-format dumps cannot be scanned.