409 Conflict: ne anlama gelir ve nasıl düzeltilir
409 Conflict, sunucunun isteği anladığı ama hedef kaynağın mevcut durumuyla çeliştiği için yerine getiremediği anlamına gelir; örneğin zaten var olan bir şeyi oluşturmak ya da daha yeni bir sürümün üzerine kaydetmek. RFC 9110 bu kodu, kullanıcının çakışmayı çözüp isteği yeniden gönderebileceği durumlar için kullanır.
409 Conflict ne anlama gelir
RFC 9110, bölüm 15.5.10, 409'u hedef kaynağın mevcut durumuyla çakışma nedeniyle tamamlanamayan bir isteğin yanıtı olarak tanımlar. Kod, kullanıcının çakışmayı çözüp yeniden gönderebileceği durumlar için tasarlanmıştır. Sunucunun, çakışmanın kaynağını açıklayan içerik göndermesi önerilir (SHOULD). RFC, çakışmanın en olası yeri olarak PUT isteğini gösterir: sunucu sürümleri izliyorsa ve PUT, başkasının daha önceki bir değişikliğiyle çakışan değişiklikler taşıyorsa sunucu 409 ile yanıt verebilir ve gövdeye istemcinin iki sürümü birleştirmek için ihtiyaç duyduğu bilgiyi koyabilir.
Durum kodu 4xx sınıfındadır, ama sorun isteğin kendisi değildir. Sözdizimi doğrudur ve değerleri geçerlidir. Yanlış olan, isteğin o andaki kaynak durumuyla uyuşmasıdır. Bunun pratik bir sonucu vardır: aynı istek, durum değiştikten sonra ya da istemci gönderdiği şeyi değiştirdikten sonra daha sonra başarılı olabilir. 422 farklıdır, çünkü aynı geçersiz veri her seferinde başarısız olur.
Hata nasıl görünür
Tarayıcıların 409 için özel bir sayfası yoktur. Çoğunlukla bir düğmenin ya da formun arkasındaki bir API yanıtıdır; insanlar onu "zaten var", "çakışma" ya da "bu öğe başka biri tarafından değiştirildi" gibi bir mesaj olarak görür. Geliştiriciler onu tarayıcının geliştirici araçlarındaki Network panelinde 409 durumlu kırmızı bir satır olarak görür. Açıklama genellikle yanıt gövdesindedir. Komut satırından:
curl -i -X POST https://api.example.com/v1/users -H "Content-Type: application/json" -d '{"email": "[email protected]", "name": "Ann"}'
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{"type": "https://api.example.com/problems/email-in-use",
"title": "Email already registered",
"status": 409,
"detail": "A user with this email address already exists."}
Gövde, application/problem+json biçimini tanımlayan RFC 9457, "Problem Details for HTTP APIs" belgesini izler. 409 döndüren bir GET isteği nadirdir. Bir 409 yanıtının gövdesi boşsa Chrome, "HTTP ERROR 409" metniyle genel "This page isn't working" sayfasını gösterir.
409 ile 400, 412, 422 ve 428 karşılaştırması
Beş durum kodu "istek bir şekilde yanlış" cevabını verir ve bazı API'ler bunları karıştırır. Farklar RFC 9110'dan, 428 için ise RFC 6585'ten gelir.
| Durum | Sunucu ne söylüyor | Tipik tetikleyici | İstemci ne yapar |
|---|---|---|---|
| 400 Bad Request | Bunu işleyemem: bozuk sözdizimi ya da geçersiz ileti çerçevelemesi. | Bozuk JSON, hatalı başlıklar, bozuk istek satırı. | İstek biçimini düzeltir. |
| 409 Conflict | İstek geçerli ama kaynağın mevcut durumuyla çakışıyor. | Yinelenen bir oluşturma, gövdede eskimiş bir sürüm, boş olmayan bir klasörü silme. | Durumu okur, çakışmayı çözer, yeniden gönderir. |
| 412 Precondition Failed | İstek başlıklarınızdaki bir koşul yanlış çıktı. | If-Match veya If-Unmodified-Since artık eşleşmiyor; var olan bir kaynakta If-None-Match: *. | Güncel sürümü ve yeni ETag'ini alır, yeniden dener. |
| 422 Unprocessable Content | Sözdizimi doğru ama içindeki talimatları işleyemem. | Bir değer doğrulamada ya da bir iş kuralında başarısız oluyor. | Veriyi değiştirir. Aynı veri yine başarısız olur. |
| 428 Precondition Required | Bu sunucu koşullu bir istek gerektiriyor. | Kayıp güncellemeleri önlemek için bir koşul isteyen bir API'de If-Match olmayan bir PUT. | Koşullu başlığı ekler. |
Aralarından seçmek için: istek yazıldığı gibi asla başarılı olamayacaksa 400 ya da 422 kullanın. Doğruysa ama şu anda yanlış olan bir duruma bağlıysa 409 kullanın. İstemci açıkça "bunu yalnızca kaynak hâlâ X sürümündeyse yap" dediyse 412 kullanın.
409 Conflict'e ne sebep olur
- Zaten var olan bir şeyi oluşturmak. Benzersiz bir e-posta, kullanıcı adı, slug, sipariş numarası ya da dosya adı alınmış.
- Daha yeni bir sürümün üzerine kaydetmek. İki kişi ya da iki tarayıcı sekmesi aynı kaydı düzenliyor ve ikinci kayıt eski bir kopyaya dayanıyor.
- Mevcut durumun izin vermediği bir eylem. Zaten kargoya verilmiş bir siparişi iptal etmek, zaten yayımlanmış bir yazıyı yayımlamak ya da içinde hâlâ dosyalar olan bir klasörü silmek.
- Eksik bir üst öğe. Var olmayan bir klasörün içinde dosya ya da klasör oluşturmak. WebDAV sunucuları böyle yanıt verir.
- Yinelenen veya paralel istekler. Gönder düğmesine çift tıklama ya da ilk istek aslında başarılı olmuşken bir zaman aşımından sonra yeniden deneme.
- Paylaşılan bir ad alanında alınmış bir ad. Bucket adları, depo adları ve proje adları bütün bir hizmet genelinde benzersizdir.
Pratikte 409 ile nerede karşılaşırsınız
Bunlar gerçek sistemlerden belgelenmiş örneklerdir. "Çakışma" anlamının API'den API'ye ne kadar değiştiğini gösterir.
- WebDAV (RFC 4918). MKCOL, ara koleksiyonlar eksik olduğunda 409 döndürür ve sunucu bunları otomatik olarak oluşturmamalıdır. Üst koleksiyonu olmayan bir kaynak oluşturacak bir PUT 409 ile başarısız olmalıdır; COPY ve MOVE de eksik hedef üst öğesi için bunu kullanır.
- Amazon S3. CreateBucket, ad alınmışsa 409 ve
BucketAlreadyExistsdöndürür, çünkü bucket ad alanı tüm kullanıcılarca paylaşılır. Bucket'ın zaten sahibiyseniz de 409 veBucketAlreadyOwnedByYoudöndürür; Kuzey Virjinya (North Virginia) dışındaki her bölgede böyledir, orada bunun yerine 200 OK döndürür. - Kubernetes API. Eskimiş bir
resourceVersiontaşıyan bir güncelleme 409 Conflict alır. API sunucusu bunu kayıp güncellemeleri tespit etmek için kullanır. - CouchDB. 409, bu kimliğe sahip bir belgenin zaten var olduğu ya da gönderdiğiniz revizyonun en son revizyon olmadığı anlamına gelir.
- Stripe. 409, isteğin başka bir istekle çakıştığı anlamına gelir; belki aynı idempotency anahtarı kullanımdadır.
- GitHub. API üzerinden bir pull request'i birleştirmek, bir
shaverdiyseniz ve pull request'in head'i onunla eşleşmiyorsa 409 döndürür. Aynı uç nokta, birleştirme yapılamadığında 405, doğrulama hatalarında 422 kullanır. - GitLab. 409, çakışan bir kaynağın zaten var olduğu anlamına gelir. API'si, silme sırasında başarısız bir
If-Unmodified-Sinceiçin 412 kullanır.
İstemciyseniz bir 409 nasıl düzeltilir
- Yanıt gövdesini okuyun. İyi bir API neyin çakıştığını söyler: hangi alan, hangi sürüm, hangi mevcut kaynak.
- Yinelenen kayıt için mevcut kaynağı alın. Oluşturmaya çalıştığınız şeyle aynıysa hedefiniz zaten gerçekleşmiştir; çağrıyı tamamlanmış sayın. Farklı bir şeyse başka bir ad ya da anahtar seçin.
- Düzenleme çakışması için güncel sürümü alın. Değişikliğinizi onunla birleştirin ya da değişikliğinizi onun üzerine yeniden uygulayın, sonra isteği yeni sürüm numarası ya da ETag ile gönderin. Asla körü körüne üzerine yazmayın, çünkü bu diğer kişinin işini siler.
- Durum çakışması için önce durumu değiştirin. Üst klasörü oluşturun, bekleyen işlemi bitirin veya iptal edin ya da klasörü boşaltın; sonra isteği tekrarlayın.
- Tekrarlanan gönderimleri durdurun. İlk tıklamadan sonra gönder düğmesini devre dışı bırakın. Ödeme tarzı isteklerde, yeniden denemenin aynı istek olarak tanınması için bir idempotency anahtarı gönderin.
- Yalnızca bir şey değiştikten sonra yeniden deneyin. Aynı isteğin körü körüne yeniden denenmesi genellikle aynı 409'u alır. İstisna, bir kilitten ya da süren bir işlemden kaynaklanan çakışmadır; orada kısa bir bekleme ve bir yeniden deneme işe yarayabilir.
Bir web uygulamasında ziyaretçiyseniz adımlar daha kısadır. Sayfayı yeniden yükleyin ve öğenizin zaten var olup olmadığına ya da başka birinin onu değiştirip değiştirmediğine bakın. Düğmeye tekrar tekrar basmayın. Başkalarıyla birlikte düzenleme yapıyorsanız metninizi kopyalayın, sayfayı yeniden yükleyin ve yeni sürümün üzerine uygulayın. Hata sürerse, siteye ne yaptığınızı ve saat kaçta yaptığınızı bildirin.
API geliştiriyorsanız 409 doğru nasıl döndürülür
409, ancak istemci neyin çakıştığını ve ne yapacağını anlayabiliyorsa işe yarar. Şu örüntüler olağan durumları kapsar.
Yinelenen oluşturma
Benzersizliği veritabanına uygulatın ve hatasını çevirin. Önce SELECT ile kontrol edip sonra eklemek, iki paralel isteğin de kontrolü geçtiği bir boşluk bırakır. Bu örnek Express ve pg paketini kullanır; PostgreSQL benzersizlik ihlalini 23505 koduyla bildirir (MySQL 1062 hatasını, SQL Server 2627 ve 2601'i kullanır):
app.post('/v1/users', async (req, res, next) => {
try {
const { rows } = await db.query(
'INSERT INTO users (email, name) VALUES ($1, $2) RETURNING id',
[req.body.email, req.body.name]
);
res.status(201).location(`/v1/users/${rows[0].id}`).json({ id: rows[0].id });
} catch (err) {
if (err.code === '23505') { // unique_violation
return res.status(409).type('application/problem+json').json({
type: 'https://api.example.com/problems/email-in-use',
title: 'Email already registered',
status: 409,
detail: 'A user with this email address already exists.',
});
}
next(err);
}
});
Herkese açık kayıt formlarında bu yanıt, hangi adreslerin hesabı olduğunu çağıran tarafa söyler. Ürününüz için bu önemliyse, adres var olsun ya da olmasın aynı şekilde yanıt verin ve gerçek sonucu e-postayla gönderin.
Sürüm çakışmaları
Düzenlenebilir her kayda bir sürüm numarası verin ve güncellemeyi buna koşullu yapın:
UPDATE documents
SET body = $1, version = version + 1
WHERE id = $2 AND version = $3;
İfade sıfır satır değiştirdiğinde başka biri önce kaydetmiştir. İstemcinin birleştirebilmesi için gövdede güncel sürümle birlikte 409 döndürün. Sürüm istek gövdesinde taşınıyorsa bu örüntüyü kullanın.
ETag ile koşullu istekler
Kayıp güncellemeleri önlemenin standart yolu HTTP'nin kendi başlıklarını kullanır. Sunucu bir ETag gönderir, istemci onu If-Match içinde geri yollar ve uyuşmazlık 409 değil 412'dir:
GET /v1/documents/42
HTTP/1.1 200 OK
ETag: "7"
PUT /v1/documents/42
If-Match: "7"
HTTP/1.1 412 Precondition Failed
Her istemcinin bir koşul göndermesini sağlamak için, If-Match olmayan bir PUT'a 428 Precondition Required ile yanıt verin. Gerçek API'lerin hepsi bu ayrımı izlemez. Kubernetes eskimiş bir resourceVersion'a 409 ile, GitLab başarısız bir If-Unmodified-Since'e 412 ile yanıt verir. Tek bir kural seçin, her yerde uygulayın ve belgeleyin.
Yanıt gövdesi
Problem details biçimini kullanın: çakışmanın türünü tanımlayan bir type URI'si, kısa bir title, status ve alanı, sürümü ya da kaynağı adlandıran bir detail. Çağıran tarafın görmesine izin verildiği durumlarda, çakışan kaynağın kimliğini veya URL'sini ekleyin. RFC 9110 tam olarak bunu ister: kullanıcının çakışmanın kaynağını tanıması için yeterli bilgi.
Dışarıdan nasıl görünür
Bir izleme kontrolü normalde bir sayfayı ya da sağlık uç noktasını okur ve bunlar hiçbir şeyle çakışmaz; bu yüzden bir kontrol sonucunda 409 görmek olağan dışıdır ve uyarıyı hak eder. Üç örüntü yaygındır:
- Bir şey oluşturan kontrol. Bir kontrol sabit bir yükle POST gönderiyorsa bir kez geçer, sonra öğe artık var olduğu için her çalışmada 409 alır. Her çalıştırma için benzersiz değerler kullanın ya da salt okunur bir uç noktayı kontrol edin.
- Dağıtımdan sonra 409. Uygulamanın yeni sürümü, eskisinin uygulamadığı bir benzersizlik ya da durum kuralını zorunlu kılıyor olabilir. Kontrol sonucunu yayından önce ve sonra karşılaştırın.
- Farklı konumlardan farklı sonuçlar. Veri çoğaltıldığında ya da yalnızca eninde sonunda tutarlı olduğunda, iki konum farklı durum görüp farklı durum kodları alabilir. Ağdan şüphelenmeden önce sonuçları konuma göre karşılaştırın.
HTTP kontrolü, bir URL'yi birkaç konumdan ister ve her biri için durum kodunu bildirir; böylece 409'un her yerde aynı olup olmadığını hemen görebilirsiniz. HostTracker 158 şehirdeki 300'den fazla kontrol noktasından kontrol çalıştırır ve bir kontrol başarısız olduğunda e-posta, SMS, sesli arama, Slack, Telegram ve daha fazlasıyla uyarı gönderir.
Sık sorulan sorular
409 bir istemci hatası mı, sunucu hatası mı?
4xx sınıfındadır; yani yanıt, isteğin gönderildiği haliyle başarılı olamayacağını söyler. Sunucu sağlıklıdır. Değişmesi gereken genellikle kaynağın durumudur; örneğin alınması ya da kaldırılması gereken mevcut bir kayıt ya da istemcinin gönderdiği veri.
Yinelenen bir e-posta 409 mu döndürmeli, 422 mi?
İkisi de kullanılır. Neden mevcut bir kaynaksa 409 uygundur: istek iyidir ama hedef durumu onunla çakışır. Değer, başka kayıtlara bağlı olmayan bir kuralı bozuyorsa, örneğin bozuk biçimli bir adres, 422 uygundur. Benzersizlik çakışmaları için birini seçin ve tüm API'de uygulayın.
Düzenleme çakışması 409 mu, 412 mi?
İstemci If-Match veya If-Unmodified-Since gönderdiyse RFC 9110 412'yi işaret eder. Çakışma gövdedeki bir sürüm numarasından ya da kaydın durumundan anlaşılıyorsa olağan yanıt 409'dur. Yukarıdaki koşullu istekler bölümü, her iki seçimi de yapan gerçek API'leri adlandırır.
409 alan bir isteği yeniden denemeli miyim?
Bir şeyi değiştirmeden hayır. Güncel durumu alın, çakışmayı çözün, sonra isteği yeniden gönderin. Hiçbir değişiklik olmadan yapılan yeniden deneme normalde aynı yanıtı alır. Bir kilitten ya da çalışan bir işlemden kaynaklanan çakışma, kısa bir beklemeden sonra geçebilir.
DELETE veya GET 409 döndürebilir mi?
DELETE döndürebilir: hâlâ dosyaları olan bir klasörü ya da başka kayıtların bağlı olduğu bir kaydı silmek bir durum çakışmasıdır. GET nadiren döndürür, çünkü okumak hiçbir şeyi değiştirmez. Bir GET 409 döndürüyorsa sunucunun o isteği nasıl ele aldığına bakın.
Var olan bir URL'ye PUT yapmak 409'a neden olur mu?
Hayır, yalnızca bu nedenle olmaz. PUT o URL'deki kaynağı değiştirir; yani "zaten var" bir çakışma değildir. PUT başka bir şeyle çakışırsa 409 çıkar; daha yeni bir sürüm, eksik bir üst öğe ya da başka bir yerde kullanılan benzersiz bir değer gibi.
Bir 409, bir sayfanın Google'dan kaldırılmasına yol açar mı?
Arama motoru botlarının (crawler) çektiği bir sayfa 409 döndürürse dizine eklenmez. Google'ın belgeleri, 4xx durum kodu döndüren URL'leri dizine eklemediğini ve zaten dizindekileri kaldırdığını söyler. Bu, arama motorlarının hiç istemediği API uç noktaları için değil, sayfalar için önemlidir.
Kaynaklar ve ek okumalar
- RFC 9110, section 15.5.10: 409 Conflict: tanım, aynı bölümdeki 400, 412 ve 422 ile birlikte.
- RFC 4918, HTTP Extensions for WebDAV: MKCOL, PUT, COPY ve MOVE için 409 durumları.
- RFC 9457, Problem Details for HTTP APIs: makine tarafından okunabilir hata gövdesi.
- Kubernetes API concepts: eskimiş bir resourceVersion'ın 409'u nasıl ürettiği.