GET /jsonl
Returns JSON Lines (NDJSON) streams with stream-level violations. Targets ML pipelines, log aggregators, and AI chat exports — anywhere line-delimited JSON is consumed. Default mode is schema drift mid-stream; other modes cover blank lines, partial final records, and BOM start.
mode
schema-drift (default; records 1–3 use {name, email}, records 4–5 use {full_name, contact.email}; schema-aware pipelines null-coerce or reject), blank-lines (empty lines between records; strict per-line parsers raise on the empty string), partial-final-line (final record truncated mid-object with no trailing newline; streaming parsers raise on EOF mid-record), bom-start (UTF-8 BOM at start of stream; strict per-line parsers see `\uFEFF{...` and raise on the first record).
control Compare against the well-formed counterpart: not.catastrophic.io/jsonl side-by-side
build a request:
expect: 200 OK with Content-Type: application/x-ndjson; charset=utf-8. Cache-Control: no-transform prevents the CDN edge from rewriting the body. X-Chaos-Jsonl-Mode reflects the selected mode; X-Chaos-Jsonl-Note explains the flaw.
curl -i 'https://chaos.catastrophic.io/jsonl?mode=schema-drift'
# Per-line json.loads is the common pattern. With schema drift, every
# line parses individually but downstream pipelines (pandas, pyarrow,
# Spark via .json()) reject the type mismatch.
import urllib.request, json
raw = urllib.request.urlopen("https://chaos.catastrophic.io/jsonl?mode=schema-drift").read().decode("utf-8")
for i, line in enumerate(raw.splitlines(), 1):
if not line:
print(f"line {i}: ")
continue
try:
rec = json.loads(line)
print(f"line {i}: keys={sorted(rec.keys())}")
except json.JSONDecodeError as e:
print(f"line {i}: parse error -> {e.msg}")
// Streaming a JSONL response with ReadableStream is the right move
// for large payloads, but for testing the chaos modes the simple
// text() + split is enough.
const res = await fetch("https://chaos.catastrophic.io/jsonl?mode=schema-drift");
const raw = await res.text();
console.log("Mode:", res.headers.get("X-Chaos-Jsonl-Mode"));
raw.split("\n").forEach((line, i) => {
if (!line) return console.log(`line ${i + 1}: `);
try {
const rec = JSON.parse(line);
console.log(`line ${i + 1}: keys=${Object.keys(rec).sort()}`);
} catch (e) {
console.log(`line ${i + 1}: parse error -> ${e.message}`);
}
});
package main
import (
"bufio"
"encoding/json"
"fmt"
"net/http"
)
func main() {
resp, _ := http.Get("https://chaos.catastrophic.io/jsonl?mode=schema-drift")
defer resp.Body.Close()
fmt.Println("Mode:", resp.Header.Get("X-Chaos-Jsonl-Mode"))
// json.Decoder.More + Decode handles trailing whitespace and EOF
// mid-token cleanly. bufio.Scanner with split-by-line gives finer
// control over blank-line handling.
dec := json.NewDecoder(resp.Body)
for i := 1; dec.More(); i++ {
var rec map[string]any
if err := dec.Decode(&rec); err != nil {
fmt.Printf("record %d: %v\n", i, err)
break
}
fmt.Printf("record %d: keys=%v\n", i, rec)
}
}
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking"] }
// serde_json = "1"
use std::io::{BufRead, BufReader};
fn main() -> Result<(), Box> {
let resp = reqwest::blocking::get("https://chaos.catastrophic.io/jsonl?mode=schema-drift")?;
let reader = BufReader::new(resp);
for (i, line) in reader.lines().enumerate() {
let line = line?;
if line.is_empty() { println!("line {}: ", i + 1); continue; }
match serde_json::from_str::(&line) {
Ok(v) => println!("line {}: {}", i + 1, v),
Err(e) => println!("line {}: parse error -> {}", i + 1, e),
}
}
Ok(())
}
// Requires Jackson: com.fasterxml.jackson.core:jackson-databind
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URI;
import java.net.http.*;
import java.io.BufferedReader;
public class JsonlChaos {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var req = HttpRequest.newBuilder(URI.create("https://chaos.catastrophic.io/jsonl?mode=schema-drift")).build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofInputStream());
var mapper = new ObjectMapper();
int i = 0;
try (var r = new BufferedReader(new java.io.InputStreamReader(resp.body()))) {
String line;
while ((line = r.readLine()) != null) {
i++;
if (line.isEmpty()) { System.out.println("line " + i + ": "); continue; }
try { System.out.println("line " + i + ": " + mapper.readTree(line)); }
catch (Exception e) { System.out.println("line " + i + ": " + e.getMessage()); }
}
}
}
}
// System.Text.Json supports DeserializeAsyncEnumerable for streaming,
// but per-line JSON via StreamReader is more straightforward.
using System.Text.Json;
using var client = new HttpClient();
using var stream = await client.GetStreamAsync("https://chaos.catastrophic.io/jsonl?mode=schema-drift");
using var reader = new StreamReader(stream);
int i = 0;
string? line;
while ((line = await reader.ReadLineAsync()) != null) {
i++;
if (line.Length == 0) { Console.WriteLine($"line {i}: "); continue; }
try {
using var doc = JsonDocument.Parse(line);
Console.WriteLine($"line {i}: {doc.RootElement}");
} catch (JsonException e) {
Console.WriteLine($"line {i}: {e.Message}");
}
}
require "net/http"
require "json"
resp = Net::HTTP.get_response(URI("https://chaos.catastrophic.io/jsonl?mode=schema-drift"))
puts "Mode: #{resp['X-Chaos-Jsonl-Mode']}"
resp.body.each_line.with_index(1) do |line, i|
line = line.strip
next puts "line #{i}: " if line.empty?
begin
rec = JSON.parse(line)
puts "line #{i}: keys=#{rec.keys.sort}"
rescue JSON::ParserError => e
puts "line #{i}: parse error -> #{e.message}"
end
end
# ConvertFrom-Json doesn't support per-line streaming directly; split
# the content and parse each line. -SkipHttpErrorCheck stops PS from
# throwing on non-2xx responses so you can still inspect the body.
$resp = Invoke-WebRequest -Uri 'https://chaos.catastrophic.io/jsonl?mode=schema-drift' -SkipHttpErrorCheck
$resp.Headers['X-Chaos-Jsonl-Mode']
$resp.Content -split "`n" | ForEach-Object -Begin { $i = 0 } -Process {
$i++
if (-not $_) { "line $($i): "; return }
try { "line $($i): $($_ | ConvertFrom-Json | ConvertTo-Json -Compress)" }
catch { "line $($i): parse error -> $($_.Exception.Message)" }
}
headers
body