Als AI stil faalt: providerfouten die eruitzien als lege resultaten

25 oktober 2026

De melding was kort: het genereren van specificaties is kapot. Je opent een taak, vraagt om een technische specificatie en krijgt een formulier terug met alle velden leeg. Geen fout, geen waarschuwing, gewoon niets. Voor wie ernaar keek, had de AI de taak bekeken en er niets over te zeggen.

De echte oorzaak had niets met die taak te maken. Het account bij de AI-provider waar het platform op draait, had geen tegoed meer, en de provider beantwoordde elk verzoek met een duidelijke melding die dat zei. Die melding kwam nooit op een scherm terecht. Om hem te vinden, moest iemand de productiesleutel met de hand tegen de API van de provider testen.

Hoe een duidelijke fout een leeg formulier werd

De code die een specificatie opstelt, ving elke exception af en gaf acht lege strings terug, één per onderdeel. Het endpoint gaf die door met een 200 OK. Aan geen enkele losse stap was technisch iets mis.

Het patroon zat niet in één functie. Onze AI-service heeft veel handlers die een fout omzetten in een leeg of standaardresultaat, en voor één soort fout is dat de juiste keuze: als het model iets teruggeeft dat we niet kunnen lezen, is een leeg concept dat iemand zelf kan invullen beter dan een crash. Voor een storing bij de provider is het precies verkeerd. Een storing geldt voor het hele platform, gaat bij de volgende poging weer mis, en de klant kan er niets aan doen. Beide hetzelfde behandelen is hoe "de provider zegt dat ons tegoed op is" veranderde in "de AI deed niets".

Runs hadden een variant van hetzelfde probleem. De command-line tool van de coding agent meldt een leeg account als gewone resultaattekst, niet als fout, en stopt daarna met een foutcode. Wij lazen alleen het veld dat het soort fout noemt, dus de uitleg werd weggegooid en de run zei "Exit code 1". Twee van onze eerste vijf runs in productie faalden zo.

Een storing onderscheiden van een leeg antwoord

Het opstellen van specificaties, de nieuwe-taak-chat en de andere AI-functies geven nu een aparte fout "AI niet beschikbaar" als de providerfout er een is die de klant niet kan oplossen: geen tegoed, een geweigerde sleutel, rate limiting. In plaats van een leeg formulier zie je een zin over wat er mis is en wat er nu gebeurt. Een antwoord dat we echt niet kunnen lezen, wordt ook niet meer opgeslagen als afgeronde specificatie; het concept wordt als mislukt gemarkeerd met de melding dat het antwoord van de AI niet te lezen was en dat je het opnieuw kunt proberen.

Bij runs bewaren we de resultaattekst van de agent ook als die zichzelf geen fout noemt, en die gaat voor op een kale exitcode. Elke runfout gaat daarna door een classifier die de ruwe tekst indeelt in een soort: geen tegoed, rate limiting, credentials van de repository geweigerd, token kan niet pushen, branch ontbreekt, taak te groot, tijd op, enzovoort. Bij elke soort hoort een samenvatting, de volgende stap, en of dezelfde run opnieuw proberen redelijkerwijs kan werken. De runpagina toont dat in plaats van de ruwe tekst. Past niets, dan zegt hij dat de run is mislukt en verwijst hij naar de log in plaats van te gokken; een verkeerde volgende stap kost meer tijd dan geen.

Zichtbaar voor ons voordat een klant het meldt

De tweede helft van de oplossing is voor onze kant. We hebben platformincidenten toegevoegd: een fout in een afhankelijkheid die wij beheren, de AI-provider, betalingen, facturatie of hosting, wordt ingedeeld en vastgelegd als één regel per soort fout, niet één per keer dat hij optreedt. Een leeg tegoed levert een fout op bij elke poging, honderden per minuut, en een regel per fout zou een log zijn, geen dashboard. Elke regel telt hoe vaak hij voorkwam en onthoudt wanneer hij voor het eerst en het laatst werd gezien. Zo beantwoordt hij wat er kapot is, sinds wanneer, en of het nog steeds gebeurt.

Actieve incidenten staan als banner boven elk tabblad van ons interne beheerpaneel, dat elke minuut ververst. Een incident verdwijnt pas als een latere aanroep naar dezelfde dienst slaagt. Dat kozen we boven verdwijnen na een vaste tijd, want een fout die stilletjes wegvalt omdat toevallig niets opnieuw werd geprobeerd, is erger dan een die iets te lang blijft staan.

Twee regels bepaalden de code. Een fout vastleggen mag nooit zelf een fout veroorzaken: het vastleggen draait binnen foutafhandeling, dus het slikt zijn eigen fouten in. En de classifier voor incidenten gebruikt dezelfde woorden als de classifier voor runfouten, zodat de banner voor beheerders en de runpagina van een klant het nooit oneens kunnen zijn over wat er misging.

Wat dit niet doet

  • Het indelen gebeurt door tekst in meldingen van providers te herkennen, omdat die providers ons geen getypeerde fouten geven. Verandert een provider zijn formulering, of zien we een fout voor het eerst, dan valt die in de algemene categorie tot we er een regel voor toevoegen.
  • De incidentbanner is voor onze eigen beheerders. Klanten zien de fout bij wat ze aan het doen waren, niet op een statuspagina van het platform.
  • Organisaties die hun eigen sleutel bij een AI-provider gebruiken, zien de fouten van hun eigen account in dezelfde woorden, maar die lossen ze zelf op, niet wij.

Bouw je op een AI-provider, dan is de controle die deze week de moeite waard is simpel: zoek elke plek waar een exception een leeg resultaat wordt, en vraag je af welke daarvan de provider kan zijn die nee zegt. Wat een run verder controleert voordat er een pull request opent, lees je in vier controles vóór een AI-pull request.

Terug naar alle artikelen

Test het op een taak uit je eigen backlog

Stuur ons één echte taak. Wij draaien CodeLynq op jouw repository en jij reviewt de pull request. Gratis, binnen 5 werkdagen.