GET /retry-mismatch
Token-keyed retry chaos. The first call for a given id returns version 1; any subsequent call for the same id within a 60-second window returns version 2 — same response shape, different data. Tests whether retry logic silently picks up changed data without noticing.
id
Client-supplied identity token (1–128 chars). State is tracked per id. Use any string you like — uuids, request IDs, anything stable across the retry.
build a request:
expect: 200 OK with JSON. First call for a given id returns data.version=1; same id within 60s returns data.version=2. X-Chaos-Retry-Replayed flips on the second call.
# First call — version 1
curl -s 'https://chaos.catastrophic.io/retry-mismatch?id=test-1' | python -m json.tool
# Second call within 60s — same id, different response
curl -s 'https://chaos.catastrophic.io/retry-mismatch?id=test-1' | python -m json.tool
import json, urllib.request
url = "https://chaos.catastrophic.io/retry-mismatch?id=test-1"
r1 = json.loads(urllib.request.urlopen(url).read())
r2 = json.loads(urllib.request.urlopen(url).read())
print(r1["data"]["version"], r2["data"]["version"]) # 1, 2
const url = "https://chaos.catastrophic.io/retry-mismatch?id=test-1";
const r1 = await (await fetch(url)).json();
const r2 = await (await fetch(url)).json();
console.log(r1.data.version, r2.data.version); // 1, 2
package main
import (
"encoding/json"
"fmt"
"net/http"
)
type response struct {
Data struct {
Version int `json:"version"`
} `json:"data"`
}
func fetch(url string) response {
resp, _ := http.Get(url)
defer resp.Body.Close()
var r response
json.NewDecoder(resp.Body).Decode(&r)
return r
}
func main() {
url := "https://chaos.catastrophic.io/retry-mismatch?id=test-1"
fmt.Println(fetch(url).Data.Version, fetch(url).Data.Version) // 1 2
}
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking", "json"] }
// serde_json = "1"
fn main() -> Result<(), Box> {
let url = "https://chaos.catastrophic.io/retry-mismatch?id=test-1";
let client = reqwest::blocking::Client::new();
let r1: serde_json::Value = client.get(url).send()?.json()?;
let r2: serde_json::Value = client.get(url).send()?.json()?;
println!("{} {}", r1["data"]["version"], r2["data"]["version"]); // 1 2
Ok(())
}
// Requires Jackson
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.*;
public class RetryMismatch {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var url = URI.create("https://chaos.catastrophic.io/retry-mismatch?id=test-1");
var mapper = new ObjectMapper();
var r1 = mapper.readTree(client.send(
HttpRequest.newBuilder(url).build(), HttpResponse.BodyHandlers.ofString()).body());
var r2 = mapper.readTree(client.send(
HttpRequest.newBuilder(url).build(), HttpResponse.BodyHandlers.ofString()).body());
System.out.println(r1.at("/data/version") + " " + r2.at("/data/version")); // 1 2
}
}
using System.Text.Json;
using var client = new HttpClient();
var url = "https://chaos.catastrophic.io/retry-mismatch?id=test-1";
async Task Version() {
var raw = await client.GetStringAsync(url);
return JsonDocument.Parse(raw).RootElement.GetProperty("data").GetProperty("version").GetInt32();
}
Console.WriteLine($"{await Version()} {await Version()}"); // 1 2
require "net/http"
require "json"
uri = URI("https://chaos.catastrophic.io/retry-mismatch?id=test-1")
fetch = -> { JSON.parse(Net::HTTP.get(uri)).dig("data", "version") }
puts "#{fetch.call} #{fetch.call}" # 1 2
$first = Invoke-WebRequest -Uri 'https://chaos.catastrophic.io/retry-mismatch?id=test-1'
$second = Invoke-WebRequest -Uri 'https://chaos.catastrophic.io/retry-mismatch?id=test-1'
($first.Content | ConvertFrom-Json).data.version # 1
($second.Content | ConvertFrom-Json).data.version # 2
$first.Headers['X-Chaos-Retry-Replayed'] # false
$second.Headers['X-Chaos-Retry-Replayed'] # true
headers
body