byrcsc/laravel-cartographer · 1.x
Troubleshooting.
What each error and warning means, why a model or an edge is missing, why a diagram will not render, and how to recover from each.
Start with the message. Every failure the command produces names its cause, and the sections below are grouped by what you are actually looking at.
Catching package exceptions
Both exceptions the package defines extend RuntimeException, as does the
writer's failure. The command catches RuntimeException and
InvalidArgumentException, prints the message, and exits 1.
use Byrcsc\Cartographer\GraphAssemblyException;
use Byrcsc\Cartographer\SchemaReadException;| Exception | Thrown when |
|---|---|
SchemaReadException | The connection cannot be opened or introspected |
GraphAssemblyException | A seed is unknown, ambiguous, or has no table; depth is negative |
InvalidArgumentException | An option or config value is invalid |
RuntimeException | The output file cannot be written |
There is no single package base exception. Catch RuntimeException when driving
the PHP API yourself and you want everything the command would have
caught.
Nothing was generated
"No Eloquent models were discovered. Check the paths configured in [cartographer.paths]."
Discovery found no class that subclasses Model. Usual causes, in order:
pathspoints somewhere that does not exist. Run without--stdoutand read the warnings — an unmatched pattern is reported by name.- Models live outside the configured directories. Add the directory, or a glob covering it.
- The autoloader cannot resolve the classes. Discovery reads class names from
the file's tokens, then asks the autoloader for them. Run
composer dump-autoloadafter moving files.
"Model discovery path matched no directories: /srv/app/src/Domain/*/Models"
A warning, not a failure. The pattern matched nothing, and the run continued. Expected when a glob covers modules that are not present in this checkout; otherwise a typo or a wrong base path.
A model is missing from the diagram
"Skipping model [App\Models\Ghost]: table [missing_ghosts] does not exist."
The class was found, but its table is not on the connection. The database is
behind the code, the model's $table is wrong, or the model belongs to a
different connection than the one being read.
php artisan migrate
php artisan cartographer:erd"Skipping model [App\Models\Report]: ..." with some other message
The model could not be instantiated — new Model threw, and the exception
message follows the colon. A constructor with required arguments will do this.
Add the model to exclude_models.
No message at all
Then the model was excluded, not skipped. Check exclude_models, and check that
the model is not abstract — abstract classes never become entities, including a
BaseModel that other models extend.
An edge is missing
Work through these in order:
- Is the related model in the diagram? An edge needs both ends. A relation
to a model outside
paths, inexclude_models, or with no table produces nothing, and says nothing. - Is the relation type excluded? Check
--exclude-relationsandrelations.exclude. Remember thatmorphandthroughare group aliases. - Is the diagram scoped? With
--models, only entities within--depthhops are kept, and edges need both ends inside the scope. - Is the method typed? With
relations.strict_types_onlyenabled, a relation method with noRelationreturn type is skipped silently. This is the one failure mode with no visible signal at all. - Does the method throw? A relation method that raises is skipped. Call it
in
tinkerto find out.
A morphTo produces no edges. Targets are resolved from the inverse side:
some discovered model has to declare a morphOne or morphMany back to this
model with the same morph name. With no inverse declared anywhere, there is
nothing to point at.
Excluding morph_many left the morph_to edges behind. Inverse relations
are read before exclusions are applied, so removing one direction does not
remove the other. Exclude morph for the whole family.
No FK markers anywhere
The edges are right and no column shows FK. That is a schema fact, not a bug:
markers come from real foreign key constraints, and applications that model
relationships only in Eloquent have none.
// A migration that produces FK markers.
$table->foreignId('author_id')->constrained('users');
// One that does not.
$table->unsignedBigInteger('author_id');SQLite needs foreign key support enabled on the connection for constraints to be
readable. Check foreign_key_constraints in config/database.php.
The connection fails
"Unable to introspect database connection [reporting]. Check the connection name, credentials, and database availability."
One message covers an unknown connection name, wrong credentials, and a database that is not running. In order:
php artisan tinker --execute="DB::connection('reporting')->getPdo();"If that fails the same way, the problem is the connection, not the package. If
it succeeds, check that cartographer.connection and --connection name the
connection you think they do — the option wins over the config key.
The original driver exception is attached to SchemaReadException as its
previous exception, so a stack trace shows the real cause.
Seed model errors
"Seed model [Postt] was not found. Did you mean: Post, User, Comment?"
No discovered model matches by short name or fully qualified name. The suggestions are ranked by edit distance against everything discovered.
"Seed model [Order] is ambiguous. Use one of: App\Models\Order, App\Domain\Sales\Models\Order."
Two discovered models share a short class name. Pass the fully qualified name, quoted so the shell leaves the backslashes alone:
php artisan cartographer:erd --models='App\Domain\Sales\Models\Order'"Seed model [Ghost] has no available database table."
The class was discovered but never became an entity, because its table is
missing. Same fix as a skipped model: migrate, or correct the model's $table.
The file cannot be written
"Unable to write ERD to [/srv/app/docs/erd.md]."
The write is atomic — a temporary file in the destination directory, then a rename — so this covers several causes:
- The path is a directory, not a file.
- The destination directory cannot be created, or is not writable by the user running the command.
- The filesystem filled up mid-write.
The previous diagram is untouched when this happens. Nothing is left half written, and the temporary file is removed.
Option and config errors
| Message | Fix |
|---|---|
Columns must be one of: all, keys, none. | Correct --columns or cartographer.columns.mode |
Format must be one of: markdown, mmd. | Correct --format or cartographer.format |
Depth must be zero or a positive integer. | --depth takes 0 or more; omit it for unlimited |
[cartographer.paths] must be an array of strings. | Fix the config value |
[cartographer.exclude_models] must contain only Eloquent model class names. | An entry is not a loadable model class |
[cartographer.columns.exclude] must map Eloquent model classes to arrays of column names. | The array is keyed by table name, or a value is a bare string |
[cartographer.relations.strict_types_only] must be a boolean. | Use true, not 'true' |
[cartographer.connection] must be null or a non-empty string. | Use null for the default connection |
The full list is in configuration.
The diagram will not render
GitHub shows the code block as text. The fence needs to be ```mermaid,
which --format=markdown produces. If you generated with --format=mmd into a
.md file, you have a raw diagram with no fence around it.
Mermaid reports a syntax error. Table and column names that are not valid
identifiers are rewritten before rendering, so this is unusual. If it happens,
regenerate with --columns=none to find out whether the problem is in the edges
or in a column line, and open an issue with the offending name.
The diagram is too large to read. Mermaid renders it; a human cannot. Scope it — see diagrams per subsystem.
The diagram changed and nothing else did
Regenerating produces a diff on an unchanged application. Check, in order:
- A different connection. A staging database with an extra column produces
a different diagram. Compare
--connectionandcartographer.connection. - A different set of migrations. A branch with an unmerged migration applied locally shows up here.
- New models on disk. A generated or scaffolded model in a scanned directory becomes an entity.
What it is not: ordering. Every list is sorted, no timestamp or host name is written into the file, and two runs on the same inputs produce identical bytes.
What to read next
- Configuration for every key and its validation.
- Console commands for options and exit codes.
- Relationship detection for why an edge exists or does not.