online / endpoints 62 / categories 10 / rate 60/min/ip /

GET /jsonl

GET /jsonl alias: /jsonl

Conformant NDJSON control. One valid JSON object per line, consistent schema across all records, LF line endings, trailing newline, no BOM. Build a client against this baseline; flip hostname to chaos.catastrophic.io to exercise the four failure modes.

expect: 200 OK, Content-Type: application/x-ndjson; charset=utf-8. Stream parses cleanly under any line-by-line JSON decoder. X-Chaos-Origin: control. X-Chaos-Counterpart points at chaos.catastrophic.io/jsonl.

bash
curl -i 'https://not.catastrophic.io/jsonl'
# 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://not.catastrophic.io/jsonl").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://not.catastrophic.io/jsonl");
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://not.catastrophic.io/jsonl")
    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://not.catastrophic.io/jsonl")?;
    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://not.catastrophic.io/jsonl")).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://not.catastrophic.io/jsonl");
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://not.catastrophic.io/jsonl"))
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://not.catastrophic.io/jsonl' -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)" }
}