Aller au contenu principal
Making DigitalMaking Digital
Note technique

MySQL errno 150 : pourquoi la clé étrangère est refusée

En bref

L'erreur errno 150 (MariaDB, MySQL 5.7), 1215 (MySQL 5.7) ou 3780 (MySQL 8.0) signifie que la clé étrangère ne correspond pas à la colonne référencée : type ou signe différent, collation différente, colonne sans index, table absente ou pas en InnoDB. Une clé primaire n'est pas obligatoire et ALTER TABLE peut ajouter la clé. Sous Laravel, id() et foreignId()->constrained() créent des colonnes compatibles.

Un import SQL ou une migration s'arrête sur une clé étrangère : "errno: 150", "Cannot add foreign key constraint" ou "are incompatible" selon la version. Ce n'est pas une question de clé primaire, et ALTER TABLE peut ajouter une clé étrangère. La clé ne correspond pas à la colonne référencée : type ou signe différent, collation différente, colonne sans index, table absente ou pas en InnoDB. Nous avons reproduit chaque cas sur MySQL 5.7, MySQL 8.0 et MariaDB 11.4.

Quel message selon votre serveur ?

Cause MySQL 5.7 MySQL 8.0 MariaDB 11.4
Types différents (INT et BIGINT UNSIGNED, ou signe différent) 1215 Cannot add foreign key constraint 3780 Referencing column ... are incompatible. 1005 errno: 150
Collation ou jeu de caractères différent 1215 3780 1005 errno: 150
Colonne référencée sans index 1215 1822 Missing index for constraint 1005 errno: 150
Table référencée absente, ou en MyISAM 1215 1824 Failed to open the referenced table 1005 errno: 150

Sous MariaDB, le message complet est Can't create table ... (errno: 150 "Foreign key constraint is incorrectly formed"). Sous MySQL 5.7, "errno: 150" apparaît dans un autre cas, vu plus bas. La documentation de MySQL résume les règles : les colonnes doivent avoir des types semblables, "The size and sign of fixed precision types such as INTEGER and DECIMAL must be the same", la longueur des chaînes peut différer, mais "the character set and collation must be the same".

Comment trouver la cause exacte ?

MySQL 8.0 nomme les colonnes en cause dans le message. Sous MySQL 5.7, le détail se trouve dans la section LATEST FOREIGN KEY ERROR :

SQL
SHOW ENGINE INNODB STATUS\G

Table absente : Cannot resolve table name close to. Type, collation ou index : un seul et même texte, Cannot find an index in the referenced table where the referenced columns appear as the first columns, or column types in the table and the referenced table do not match. Comparez alors les deux colonnes avec SHOW CREATE TABLE sur chaque table.

Les deux colonnes ont-elles vraiment le même type ?

Avec Laravel, le cas typique : $table->id() crée un BIGINT UNSIGNED, et une colonne INT ou BIGINT signée ne peut pas le référencer. Cette table est acceptée :

SQL
CREATE TABLE users (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB;
CREATE TABLE posts (id INT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED, FOREIGN KEY (user_id) REFERENCES users(id)) ENGINE=InnoDB;

Avec user_id INT ou user_id BIGINT, les trois serveurs la refusent. Pour du texte, un VARCHAR(20) qui référence un VARCHAR(10) passe, mais pas une collation utf8mb4_general_ci face à utf8mb4_unicode_ci, ni utf8 face à utf8mb4, ce qui arrive lors d'un import entre deux serveurs aux réglages différents.

Si la colonne existe déjà, corrigez son type puis ajoutez la clé :

SQL
ALTER TABLE posts MODIFY user_id BIGINT UNSIGNED;
ALTER TABLE posts ADD CONSTRAINT posts_user_id_foreign FOREIGN KEY (user_id) REFERENCES users(id);

La colonne référencée a-t-elle un index ?

La colonne référencée n'a pas besoin d'être une clé primaire : il lui faut un index dont elle est la première colonne. La documentation : "InnoDB permits a foreign key to reference any index column or group of columns". Une clé vers users.email sans index échoue ; avec INDEX (email) dans la table users, elle passe.

La table référencée existe-t-elle, et en InnoDB ?

Une clé vers une table pas encore créée ou en MyISAM échoue. Plus sournois : si la table qui référence est en MyISAM, CREATE TABLE réussit, mais aucune clé n'est créée (0 contrainte dans information_schema.REFERENTIAL_CONSTRAINTS).

Un fichier d'import peut désactiver les contrôles avec SET foreign_key_checks=0 : posts a alors pu être créée avant users, avec sa clé. Ce n'est pas un passe-droit pour autant :

  • si users est ensuite créée avec un id INT, MySQL 5.7 et 8.0 refusent la table, alors que MariaDB l'accepte sans rien dire ;
  • sous MySQL 5.7, modifier le type d'une colonne liée renvoie ERROR 1025 (HY000): Error on rename of ... (errno: 150 - Foreign key constraint is incorrectly formed) ;
  • changer la collation de la table référencée (CONVERT TO CHARACTER SET) passe sur MySQL 5.7 et 8.0, et laisse une clé entre deux collations différentes.

Et dans une migration Laravel ?

Laravel crée la table, puis ajoute la clé par un ALTER TABLE : l'erreur cite la requête en cause, un alter table qui ajoute la contrainte articles_author_id_foreign. Deux causes reviennent :

  • L'ordre des migrations. Elles s'exécutent dans l'ordre de l'horodatage du nom de fichier : une migration articles datée avant authors échoue (1215 sous MySQL 5.7).
  • Un type ancien. Une table créée avec $table->increments('id') (INT UNSIGNED) ne peut pas être référencée par foreignId() (BIGINT UNSIGNED).

La forme courte de la documentation crée la bonne colonne et la bonne clé :

PHP
// database/migrations/2026_10_19_110000_create_articles_table.php
Schema::create('articles', function (Blueprint $table) {
    $table->id();
    $table->foreignId('author_id')->constrained();
});

Résultat : author_id bigint(20) unsigned NOT NULL et la contrainte articles_author_id_foreign. Si la table référencée date d'avant et utilise increments, déclarez une colonne du même type (INT UNSIGNED) plutôt que foreignId().

La migration a échoué à moitié

Après l'échec, la table articles existait déjà, sans sa clé, et relancer php artisan migrate a donné 1050 Table 'articles' already exists. Supprimez cette table avant de relancer.

Ces erreurs surgissent lors d'une reprise de données, étape que nous gérons dans nos applications sur mesure comme quand nous connectons des logiciels. Une base à reprendre ? Parlons-en.

À retenir
  • errno 150, 1215, 3780 : la clé ne correspond pas à la colonne référencée, pas un problème de clé primaire.
  • Même type, même signe, même collation ; la longueur des VARCHAR peut différer.
  • La colonne référencée doit avoir un index, la table doit exister et être en InnoDB.
  • ALTER TABLE ... ADD FOREIGN KEY fonctionne ; sous Laravel, id() et foreignId()->constrained() vont ensemble.

Testé avec MySQL 5.7.33, MySQL 8.0.46, MariaDB 11.4.13, Laravel 13.37.0 et PHP 8.3.6.

Questions fréquentes

Que signifie "Cannot add or update a child row: a foreign key constraint fails" ?

C'est l'erreur 1452 : la clé est bien formée, mais des lignes existantes pointent vers un identifiant absent de la table référencée. Dans notre test, un ALTER TABLE ... ADD FOREIGN KEY a échoué ainsi à cause d'une ligne orpheline : corrigez ou supprimez ces lignes avant d'ajouter la clé.

Peut-on importer une base avec foreign_key_checks=0 sans risque ?

C'est pratique pour créer les tables dans n'importe quel ordre, mais la documentation de MySQL prévient que les lignes ajoutées pendant ce temps ne sont pas vérifiées quand les contrôles sont réactivés. Les incohérences restent alors dans la base.

Pourquoi MySQL refuse de modifier la colonne id ou de supprimer un index ?

Parce qu'une clé étrangère s'en sert. Dans nos tests : ERROR 1833 Cannot change column 'id': used in a foreign key constraint sous MySQL 5.7 et MariaDB (3780 sous MySQL 8.0), et ERROR 1553 Cannot drop index pour l'index. Supprimez la clé, faites la modification sur les deux colonnes, puis recréez la clé.

Making Digital

Nous connectons les logiciels des PME, automatisons leurs tâches répétitives et créons des assistants IA, depuis 2010. Nos articles partent de nos projets réels.

Notre parcours
Une question sur ce sujet ?

Parlons-en 30 minutes.

Vous saurez si votre problème se règle, comment, et en combien de temps. Sans engagement.

Un échange de 30 minutes, en visio ou par téléphone

Gratuit, sans engagement. On parle de votre problème, pas de technique.