---
title: "API hallucinée"
type: "ai-smell"
slug: "hallucinated-api"
url: "http://localhost:3000/fr/ai-smells/hallucinated-api.md"
category: "Exactitude"
description: "Code généré par IA qui appelle des fonctions, méthodes, paramètres, clés de configuration ou paquets qui semblent plausibles mais n'existent pas dans la version réelle de la bibliothèque dont vous dépendez."
---
# API hallucinée

> Code généré par IA qui appelle des fonctions, méthodes, paramètres, clés de configuration ou paquets qui semblent plausibles mais n'existent pas dans la version réelle de la bibliothèque dont vous dépendez.

## Signs and Symptoms

Un relecteur repère une API hallucinée quand le code se lit avec fluidité et « ressemble » à un usage idiomatique d'une bibliothèque, alors que l'appel, l'option ou l'import précis est introuvable dans la surface réelle de cette bibliothèque. Signes révélateurs :

* Une méthode ou une option dont le nom est _trop pratique_ — elle fait exactement ce que le prompt demandait, avec un nom qui mélange deux API réelles (par ex. `findLastWhere`, `includesAll`, `parseDateSafe`).
* Des **paramètres** inventés sur une fonction réelle (la variante dangereuse — elle compile souvent et n'échoue qu'à l'exécution, ou ignore silencieusement l'option).
* Un `import` d'un paquet qui n'est pas dans `package.json` / `requirements.txt`, ou un import nommé que le module n'exporte jamais.
* Des formes d'API qui mélangent les versions : une signature v2 appelée sur un client v5, ou une méthode supprimée/renommée plusieurs versions auparavant.
* Des commentaires inline confiants affirmant que l'appel est correct (« // renvoie la charge remboursée »).

```js
// Généré par IA — fluide, plausible, et faux
import { formatRelative } from 'date-fns';

// `roundingMethod` est inventé — date-fns n'expose aucune option de ce nom, elle est silencieusement ignorée
const label = formatRelative(date, new Date(), { roundingMethod: 'floor' });

// `findLastWhere` n'existe pas sur Array — TypeError à l'exécution
const lastActive = items.findLastWhere(i => i.active);

// Appel SDK mélangé : la vraie forme Stripe est stripe.refunds.create({ charge })
await stripe.charges.refund(chargeId, { amount: 500 });

```

Une heuristique rapide : si vous ne pouvez pas pointer vers l'entrée de documentation ou la définition de type d'un appel tiers en moins d'une minute, traitez-le comme halluciné jusqu'à preuve du contraire.

## Reasons for the Problem

**Pourquoi les modèles le produisent**

* **Plausibilité du token suivant, pas une recherche.** Un LLM prédit la continuation statistiquement la plus probable, ce qui revient à la _moyenne_ de chaque API similaire qu'il a vue. Cette moyenne est souvent une méthode qui _devrait_ exister — le modèle émet « le nom pratique » plutôt que le vrai. Les études sur la recommandation d'API constatent que **58,1 % à 84,1 % des API recommandées n'existent pas dans le paquet nommé**, et l'erreur dominante est le nom de méthode inexistant ([arXiv 2404.00971](https://arxiv.org/pdf/2404.00971), [ACM TOSEM 2025](https://dl.acm.org/doi/pdf/10.1145/3728894)).
* **Mélange de versions.** Les corpus d'entraînement mêlent de nombreuses versions d'une bibliothèque, donc le modèle fusionne les signatures v2 et v5 en une seule qui ne correspond à aucune. Ces « hallucinations en conflit avec la connaissance » (par ex. des paramètres inexistants) sont précisément le genre qui **passe à travers les linters et échoue à l'exécution**.
* **Obsolescence liée à la date limite d'entraînement.** Le modèle utilise avec assurance des API renommées, dépréciées ou supprimées depuis sa date limite, et se rabat sur ce qui apparaissait le plus dans le corpus — ce qui, selon OX Security, signifie que **des versions de paquets plus anciennes, parfois vulnérables, sont recommandées** ([rapport OX, oct. 2025](https://www.ox.security/blog/ai-code-security-common-threats-and-best-practices-for-securing-ai-generated-code/)).
* **Aucun contexte de dépôt/dépendances.** Sans votre `package.json` ni la source réelle du module, le modèle invente des helpers qui « semblent » appartenir à votre stack.
* **Complaisance / empressement à répondre.** L'assistant ne dit presque jamais « je ne suis pas sûr que cette méthode existe » — il produit un code confiant et d'apparence exécutable, ce qui abaisse la méfiance du relecteur.
* **Le déterminisme le rend exploitable.** L'hallucination de paquet n'est pas un bruit aléatoire : sur 16 modèles et 2,23 M de générations, **19,7 % des paquets recommandés étaient fictifs (205 474 noms uniques), et 58 % des hallucinations se reproduisaient en l'espace de 10 relances** ([USENIX Security 2025 ; synthèse SecurityWeek](https://www.securityweek.com/ai-hallucinations-create-a-new-software-supply-chain-threat/)).

**Pourquoi c'est nuisible**

* **Exactitude.** Des paramètres inventés et des options silencieusement ignorées produisent un comportement erroné sur des chemins de code que les tests couvrent rarement ; la défaillance apparaît en production, pas à la compilation.
* **Sécurité / chaîne d'approvisionnement.** Un nom de paquet halluciné est une cible d'enregistrement : les attaquants publient des logiciels malveillants sous le nom prédit, de sorte que le prochain développeur qui accepte la suggestion l'installe — l'attaque **slopsquatting** (terme forgé par Seth Larson de la PSF). La variante d'obsolescence réintroduit silencieusement des API dépréciées ou porteuses de CVE.
* **Charge de revue.** Un code plausible reporte le fardeau sur les relecteurs, qui doivent vérifier chaque appel inhabituel dans la documentation ; une prose fluide rend cette vérification _moins_ probable.
* **Accumulation de dette technique.** Les développeurs collent souvent le stub halluciné « presque fonctionnel » et bricolent autour plutôt que de corriger l'appel à la racine — alimentant la tendance plus large de l'ère IA : hausse du copier-coller et baisse de la refactorisation ([GitClear 2025 : clones multipliés par \~8×, lignes déplacées par refactorisation passées de 25 % à <10 %](https://www.gitclear.com/ai%5Fassistant%5Fcode%5Fquality%5F2025%5Fresearch)). Ce pattern est désormais répertorié parmi les code smells spécifiques à l'IA ([arXiv 2509.20491](https://arxiv.org/abs/2509.20491)).

## Treatment

**Tactiques de processus & de prompting**

* **Ancrez le modèle dans des surfaces réelles.** Collez les vrais stubs de types, la page de documentation pertinente ou la source de la version installée dans le contexte, ou utilisez un outil de récupération de documentation (style Context7 / RAG). Indiquez-lui les versions _exactes_ des dépendances issues de votre fichier de verrouillage.
* **Exigez des citations.** Demandez au modèle de nommer l'entrée de documentation officielle ou la signature de type pour chaque appel tiers qu'il utilise. Règle empirique des praticiens : _chaque appel de méthode sur une bibliothèque tierce doit être retracé jusqu'à son entrée de documentation avant l'approbation de la PR._
* **Faites-le tourner, dans la boucle.** Exigez que `tsc` / `mypy` / `pylint`, le build et la suite de tests passent, et faites en sorte que l'agent exécute réellement `npm install` / `pip install` pour qu'un paquet halluciné échoue tôt plutôt que d'atteindre la revue.
* **Demandez-lui de réutiliser, pas d'inventer.** Donnez-lui un `grep` du module existant et demandez-lui d'appeler les helpers existants (`réutilise les helpers de utils/http.ts`) au lieu d'en conjurer de nouveaux — cela contre aussi le smell connexe de duplication par _helper réinventé_.
* **Verrouillez les dépendances.** Utilisez un fichier de verrouillage + une liste blanche d'installation et un scanner de chaîne d'approvisionnement (Socket/Snyk) avant l'ajout de tout nouveau paquet, afin que des noms slopsquattés ne puissent pas se glisser.

**Le refactoring du code**

Remplacez l'appel inventé par le véritable, vérifié. Si vous voulez vraiment la commodité que le modèle a imaginée, implémentez-la une seule fois contre l'API réelle derrière un wrapper **Extraire une fonction** au lieu de disperser l'appel fictif.

```ts
// Avant — paramètre halluciné + forme de SDK mélangée
async function refundLast(chargeId: string) {
  // `stripe.charges.refund` et cette forme d'option n'existent pas
  return stripe.charges.refund(chargeId, { amount: 500, reason: 'requested' });
}

// Après — vérifié contre les types/docs du SDK Stripe installé,
// et la « commodité » encapsulée une fois pour ne pas répéter la vraie API
async function refundCharge(chargeId: string, amountCents: number) {
  return stripe.refunds.create({
    charge: chargeId,
    amount: amountCents,
    reason: 'requested_by_customer',
  });
}

```

Pour la variante d'obsolescence, traitez un appel déprécié signalé comme une véritable tâche de mise à niveau : passez à l'API actuelle et épinglez la version, au lieu de faire taire l'avertissement.

**Limites.** Les vérificateurs de types et les résolveurs attrapent le sous-ensemble _résoluble_ (objets typés, imports non résolus). Le reste dynamique/non typé — options inventées sur `any`, clés de configuration en chaînes, champs de payload REST — n'a **aucun détecteur automatisé fiable** et doit être vérifié par un humain contre la vraie documentation.

## Detected by

- **eslint-plugin-import** `import/no-unresolved` — Import non résolu (https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-unresolved.md)
- **eslint-plugin-import** `import/named` — Export nommé inexistant (https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/named.md)
- **typescript-eslint** `@typescript-eslint/no-deprecated` — Usage d'une API dépréciée (réelle mais périmée) (https://typescript-eslint.io/rules/no-deprecated/)
- **Pylint** `no-member (E1101)` — Accès à un membre non défini (https://pylint.readthedocs.io/en/stable/user_guide/messages/error/no-member.html)
- **mypy** `attr-defined` — Attribut/méthode non défini sur le type (https://mypy.readthedocs.io/en/stable/error_code_list.html#check-that-attribute-exists-attr-defined)
