# VC-105 · Uçların hata yanıtı tutarsız, istemci başarısız işlemi başarılı sanıyor

Sunucu hatayı gövdeye yazıyor ama başarılı HTTP durumu döndürüyor. İstemci yalnız isteğin tamamlanmasına bakınca kullanıcıya kaydın oluştuğunu söylüyor, gerçek işlem başarısız kalıyor.

- Önem: ORTA. Etkisi orta. Trafik artınca ya da istek tekrarlanınca tetiklenir.
- Önem notu: Başarısız dış yazının başarılı yanıt gibi yorumlanması esas alınır. Etki işlemin para veya veri değiştirmesine göre artar. Bilerek tanımlanmış zarf protokolü varsa istemcinin bu sözleşmeyi gerçekten uygulaması değerlendirilir.
- Eksen ve kategori: Sağlamlık, 16 Sözleşmeler ve testler
- Yığın: Her yığın
- Yapay zekâ kodunda: ölçülmedi. Dayanak: uzman görüşü.
- Ne zaman bakılır: İlk yayından önce, Her ay
- CWE: CWE-394
- OWASP Top 10:2025: A10:2025 Mishandling of Exceptional Conditions
- Checklist ifadesi: Uçların hata yanıtları tutarlı, istemci başarısız bir işlemi başarılı sanmıyor.
- Son inceleme: 4 Ekim 2026, Komünite editörlüğü
- Adres: https://vibecheck.komunite.com.tr/madde/hata-yaniti-tutarsiz-istemci-basari-saniyor

## 60 saniyelik kontrol

Yalnız kendi uygulamanda ya da yazılı izin aldığın sistemde dene. Bu bir sızma testi değildir.

1. Başarısız kaydetme yanıtının gerçek HTTP durumunu kontrol et.
2. İstemcinin response.ok ve başarı gövdesini doğruladığını incele.
3. Hata yanıtında parola, token veya iç hata metni bulunmadığını kontrol et.
4. Bozuk JSON veya beklenmeyen HTML yanıtın başarıya dönüşmediğini doğrula.

## Ne oluyor

Kullanıcı kaydet düğmesine basıyor. Sunucudaki yazma başarısız oluyor, fakat uç hata metnini başarılı HTTP yanıtının içinde gönderiyor. İstemci isteğin tamamlanmasını başarı sanıp kaydedildi bildirimi gösteriyor. Kullanıcı ekranı kapattığında verisi kalıcı olarak oluşmamış. Arayüzün verdiği sözle gerçek işlem sonucu birbirinden ayrılmış oluyor.

Benzer sorun uçlar farklı hata biçimleri kullandığında ortaya çıkar. Biri `error`, diğeri `message`, başkası boş gövde döndürüyor olabilir. İstemci yalnız alıştığı biçimi tanıyorsa beklenmeyen yanıtı yanlış yorumlayabilir. Bir ara katmanın HTML hata sayfası göndermesi de aynı sözleşme sınırına girer. JSON okuyabilmek veya Promise'in tamamlanması, işin başarılı olduğu anlamına gelmez.

Sağlam yanıt sözleşmesi durum kodunu, gövdeyi ve istemcinin kararını birlikte tanımlar. Normal kayıt hangi alanla doğrulanacak? Geçici hizmet hatası nasıl temsil edilecek? Bozuk gövde geldiğinde kullanıcıya başarı gösterilecek mi? Bu sorular ortak cevaplanmadığında her uç kendi başına makul görünürken uygulamanın bütünü hatalı davranabilir. Düzeltme hem üreticiyi hem tüketiciyi kapsamalıdır.

## Gerçek olay

Bu madde belirli bir AI uygulamasının kamuya açık veri kaybı vakasına dayanmıyor. Fetch belgesi, HTTP hata durumlarının isteğin Promise'ini otomatik olarak reddettirmediğini anlatıyor. İstemci yanıtın durumunu ayrıca değerlendirmelidir. [Fetch kullanımı](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch)

RFC 9457, HTTP API hataları için Problem Details biçimini tanımlar. Gövdede durum alanı kullanılıyorsa üreten sunucunun gerçek HTTP durumuyla aynı olması gerekir. [Problem Details](https://www.rfc-editor.org/rfc/rfc9457.html) Buradaki örnek bu yaklaşımı küçük bir kaydetme işleminde uygular. CWE-394 eşlemesi, tüketicinin beklenmeyen dönüş değerini uygun biçimde denetlememesiyle ilişkilidir. [Beklenmeyen dönüşün kontrolü](https://cwe.mitre.org/data/definitions/394.html) Yalnız standart alan adlarını kullanmak bütün hata yollarını doğru yapmaz. İstemcinin gerçek kararı ve başarısız işlem sonrası davranışı da sınanmalıdır.

## Yapay zekâ bunu neden üretiyor

**Model hatayı JSON'a çevirmeyi yeterli görebilir.** Ajan `catch` içinde okunabilir bir hata gövdesi döndürür ve uç artık çökmüyor gibi görünür. Fakat yanıt yardımcısının varsayılan başarılı durumunu değiştirmeyi atlayabilir. Sunucu kodu hata içerirken HTTP katmanı başarı bildirir. İstemcinin hangi alanı kullandığı incelenmeden yapılan bu düzenleme eksik kalır. Hata yakalamakla doğru sözleşme üretmek ayrı adımlardır.

**İstemci Promise sonucunu iş sonucu sanabilir.** Ajan ağ çağrısını `await` ile bekledikten sonra başarı bildirimi ekleyebilir. Bu akış bağlantının yanıt aldığını gösterir, kaydın oluştuğunu göstermez. HTTP durumu ve başarı verisi ayrıca doğrulanmalıdır. Test yalnız sahte başarılı yanıt döndürüyorsa bu fark görünmez. Başarısız ve bozuk yanıtların da aynı tüketiciye verilmesi gerekir.

**Uçlar farklı örneklerden üretilir.** Bir ekran için yazılan hata yapısı, başka ekranda farklı bir adla ortaya çıkabilir. Model her görevi yerel olarak çözerken ortak yanıt sözleşmesi aramayabilir. Sonradan genel istemci yardımcısı eklemek de eski biçimleri otomatik düzeltmez. Üreten ve tüketen yollar birlikte listelenmeli, geçiş sırasında hangi biçimlerin kabul edildiği açık tutulmalıdır. Sessiz varsayılan başarı bu geçişi gizlememelidir.

**İç hata metni kullanıcı mesajına taşınabilir.** Hızlı açıklama üretmek için exception'ın metni doğrudan yanıta konabilir. Bu metin bağlantı, sorgu veya sır ayrıntısı içerebilir. Sabit ve güvenli hata koduyla kullanıcı mesajını ayırmak bu riski azaltır. Bu açıklamalar üretim mekanizmasına ilişkin çıkarımlardır. AI araçlarında ölçülmüş yaygınlık veya bu hatanın her üretilen uçta bulunduğu iddiası değildir.

## Etki

Kullanıcı kaydının oluştuğuna inanıp işlemden ayrılabilir. Daha sonra eksik veri fark edildiğinde aynı işi yeniden yapmak gerekir. İstemci başarısızlığı başarı diye sayıyorsa izleme grafikleri de gerçek sorunları eksik gösterebilir. Başka bir iş, oluşmamış kayda dayanarak devam edebilir.

Hatanın yanlış sınıflanması gereksiz yeniden denemeyi de tetikleyebilir. Geçici hizmet sorunu ile geçersiz kullanıcı girdisi aynı davranışı gerektirmez. Öte yandan hata gövdesine çok ayrıntı koymak sorunu çözmez. Kullanıcının karar verebilmesi için gerekli bilgi ile sunucunun inceleme verisi ayrı tutulmalıdır. Bu madde veri yazısının kendisini değil, sonucunun aktarılmasını ele alır.

## Nasıl anlarsın

Ayrı test ortamında yazma bağdaştırıcısını kontrollü hata verecek şekilde çalıştır. Dönen gerçek HTTP durumu, gövde ve istemcinin gösterdiği sonuç birbiriyle uyumlu mu? Başarılı durum içinde hata alanı dönüyorsa istemcinin bunu nasıl yorumladığını izle. Yalnız sunucu günlüğündeki hata metnine bakma.

Yerel örnek gerçek `Response` nesnelerini kullanır. Normal kayıt başarı verisiyle döner. Yazma hatası Problem Details gövdesi ve uygun HTTP durumuyla temsil edilir. Test, iç hata metninin yanıta taşınmadığını da kontrol eder. Hatalı zarf ve HTML yanıt başarıya dönüşmez. Gerçek ağ veya tarayıcı bildirimi bu deneyin dışında kalır.

## Nasıl düzeltirsin

1. **Sonuçları tanımla.** Başarı, geçersiz istek, yetki reddi ve hizmet arızası için anlamlı davranış belirle. Aynı duruma uçtan uca aynı karşılığı ver.
2. **Durumu gövdeyle eşleştir.** Hata gövdesi döndürürken başarılı HTTP durumunu varsayılan bırakma. Ortak yardımcı kullanıyorsan gerçek yanıtı test et.
3. **İstemciyi doğrula.** Önce HTTP durumunu, ardından başarı gövdesinin gerekli alanlarını kontrol et. Bozuk JSON veya beklenmeyen veri başarı dalına girmesin.
4. **Ayrıntıyı sınırla.** Kullanıcıya sabit güvenli kod ver. İnceleme için gereken teknik ayrıntıyı erişimi sınırlı sunucu kanalında tut.
5. **İki yolu birlikte dene.** Normal işlemin çalıştığını ve hata yolunda başarı bildirimi üretilmediğini doğrula. Dış yan etki varsa onun sonucunu da karşılaştır.

## Bir daha olmasın

Yeni uç eklenirken istemciyle ortak hata sözleşmesini kullan. Başarı ve hata yanıtını aynı kabul testinde karşılaştır.

## Sınır

Bazı protokoller HTTP başarısı içinde ayrı iş sonucu zarfı tanımlar. Bu tasarım, istemci sözleşmeyi eksiksiz uyguluyorsa otomatik bulgu değildir. Örnek yalnız tek kayıt kimliği taşıyan başarı gövdesini doğrular. Gerçek uygulama bütün şemasını kontrol etmelidir. Transaction, yeniden deneme, yetki ve idempotency sorunları doğru hata yanıtıyla kendiliğinden çözülmez. Yerel yanıt testi canlı dağıtımın çalıştığını kanıtlamaz.

## Düzeltme kodları

### Node.js: HTTP durumu ve yanıt sözleşmesi

Önce:

```js
// api/kaydet.js, açıklama amaçlı. save kimliği doğrulanmış yazma bağdaştırıcısıdır.
export async function handleSave(save) {
  try {
    const data = await save();
    return Response.json({ data }, { status: 201 });
  } catch {
    // HTTP başarı durumunda hata gövdesi dönüyor.
    return Response.json({ error: 'Yazma başarısız' });
  }
}
export async function readResult(response) {
  // İstek veya JSON okumasının tamamlanması işin başarısı sayılıyor.
  const body = await response.json();
  return { saved: true, data: body.data };
}
```

Sonra:

```js
// api/kaydet.js, açıklama amaçlı. save kimliği doğrulanmış yazma bağdaştırıcısıdır.
export async function handleSave(save) {
  try {
    const data = await save();
    return Response.json({ data }, { status: 201 });
  } catch {
    return Response.json({ type: 'about:blank', title: 'Service Unavailable',
      status: 503, code: 'STORAGE_UNAVAILABLE' }, {
      status: 503, headers: { 'Content-Type': 'application/problem+json' },
    });
  }
}
export async function readResult(response) {
  if (!response.ok) throw new Error('REQUEST_FAILED');
  let body;
  try { body = await response.json(); } catch { throw new Error('INVALID_RESPONSE'); }
  if (!body || typeof body.data?.id !== 'string' || !body.data.id || 'error' in body) {
    throw new Error('INVALID_RESPONSE');
  }
  return { saved: true, data: body.data };
}
// Üretimde tüm başarı şemasını doğrula. Bu örneğin kaydı yalnız id taşır.
// Hata kaydı güvenli sunucu kanalında tutulur, exception metni yanıta eklenmez.
```

Düzeltmeyi kanıtlayan test:

```js
// api/kaydet.test.mjs, açıklama amaçlı. Gerçek Response ve yerel bağdaştırıcı.
import test from 'node:test';
import assert from 'node:assert/strict';
const { handleSave, readResult } = await import(process.env.ORNEK_DOSYA);
test('başarı ve hata ayrılır, bozuk yanıt başarı üretemez', async () => {
  let writes = 0;
  const ok = await handleSave(async () => { writes++; return { id: 'local-1' }; });
  assert.equal(ok.status, 201);
  assert.deepEqual(await readResult(ok), { saved: true, data: { id: 'local-1' } });
  const failed = await handleSave(async () => { throw new Error('password=sentetik-sir'); });
  assert.equal(failed.status, 503);
  assert.equal(failed.headers.get('content-type'), 'application/problem+json');
  const problem = await failed.clone().json();
  assert.equal(problem.status, failed.status);
  assert.equal(problem.code, 'STORAGE_UNAVAILABLE');
  assert.ok(!JSON.stringify(problem).includes('sentetik-sir'));
  await assert.rejects(readResult(failed), /REQUEST_FAILED/);
  await assert.rejects(readResult(Response.json({ error: 'Hata' })), /INVALID_RESPONSE/);
  await assert.rejects(readResult(new Response('<html>Proxy</html>')), /INVALID_RESPONSE/);
  await assert.rejects(readResult(new Response('Proxy', { status: 502 })), /REQUEST_FAILED/);
  assert.equal(writes, 1);
});
```

## Ajan kuralı (AGENTS.md)

```md
## Hata başarı gibi dönüyor (vibecheck VC-105)
- HTTP durumunu işlemin sonucuyla tutarlı tut.
- Hata gövdesi için kararlı bir sözleşme kullan.
- İstemcide HTTP durumu ve başarı verisini doğrula.
- İç hata ayrıntısını kullanıcı yanıtına koyma.
- Normal, hata ve bozuk yanıt yollarını birlikte sınayacak test yaz.
```

## Kaynaklar

1. [RFC 9457 Problem Details for HTTP APIs](https://www.rfc-editor.org/rfc/rfc9457.html), IETF, Temmuz 2023
2. [Using the Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch), MDN Web Docs
3. [CWE-394 Unexpected Status Code or Return Value](https://cwe.mitre.org/data/definitions/394.html), MITRE

---

vibecheck · Komünite editörlüğü. Metin CC BY 4.0, prompt ve kural parçaları MIT-0. Kaynak: https://vibecheck.komunite.com.tr/madde/hata-yaniti-tutarsiz-istemci-basari-saniyor
