Skip to main content

Chaos Engineering

agnt includes a built-in chaos engineering system for testing how your frontend handles network failures, slow connections, and unreliable APIs. Test the unhappy paths without changing your code.

Why Chaos Testing for Frontend?

Most frontend bugs happen under conditions developers never test:

  • Slow networks - 3G connections, hotel WiFi, subway tunnels
  • Flaky APIs - 500 errors, timeouts, rate limits
  • Race conditions - Responses arriving out of order
  • Stale tabs - Browser tabs open for hours with expired tokens

agnt lets you simulate all of these with a single command.

Quick Start

# Start proxy, then apply a chaos preset
proxy {action: "start", id: "app", target_url: "http://localhost:3000"}
proxy {action: "chaos", id: "app", chaos_operation: "preset", chaos_preset: "flaky-api"}

Your app now experiences random 500 errors, timeouts, and variable latency. Watch what breaks.

All chaos commands route through proxy {action: "chaos"} with a chaos_operation field that selects what to do. Valid operations: enable, disable, status, set, preset, add_rule, remove_rule, list_rules, stats, clear.

OperationRequired fieldPurpose
presetchaos_presetApply a named bundle of rules
setchaos_configReplace the full rule set (with optional seed)
add_rulechaos_ruleAdd a single rule
remove_rulechaos_rule_idRemove one rule by id
enable / disableToggle chaos on/off without losing rules
status / statsInspect current state and injection counts
list_rulesList active rules
clearRemove all rules

Built-in Presets

PresetWhat It SimulatesUse For
mobile-3g200-2000ms latency, 2% packet lossMobile network testing
mobile-4g50-500ms latency, 0.5% packet lossLTE network testing
flaky-apiRandom 500s, timeouts, variable latencyAPI resilience testing
race-conditionOut-of-order responses, high variance delaysRace condition bugs
stale-tab3-hour delaysToken expiry, stale state
slow-connection5KB/s bandwidth throttlingSlow network handling
connection-drops10% mid-response disconnectsRetry logic testing
data-corruption5% truncated responsesPartial data handling
rate-limited20% 429 errorsRate limit UI testing
auth-failures10% 401/403 errorsAuth error handling
service-degradationMixed latency + errors + truncationGraceful degradation
pressure-testEverything at onceStress testing

Preset Examples

Test mobile network conditions:

proxy {action: "chaos", id: "app", chaos_operation: "preset", chaos_preset: "mobile-3g"}
# Your app now has 200-2000ms latency and 2% packet loss

Expose race conditions:

proxy {action: "chaos", id: "app", chaos_operation: "preset", chaos_preset: "race-condition"}
# API responses arrive out of order - does your state get corrupted?

Test stale browser tabs:

proxy {action: "chaos", id: "app", chaos_operation: "preset", chaos_preset: "stale-tab"}
# Simulates a tab that's been open for hours - test token refresh

Custom Chaos Rules

For fine-grained control, use chaos_operation: "set" with a chaos_config that holds the full rule set. set replaces all existing rules:

proxy {
action: "chaos",
id: "app",
chaos_operation: "set",
chaos_config: {
"enabled": true,
"rules": [
{
"id": "api-latency",
"name": "Slow API calls",
"type": "latency",
"enabled": true,
"url_pattern": "/api/.*",
"min_latency_ms": 500,
"max_latency_ms": 3000,
"probability": 0.5
},
{
"id": "checkout-errors",
"name": "Checkout failures",
"type": "http_error",
"enabled": true,
"url_pattern": "/api/checkout",
"error_codes": [500, 503],
"probability": 0.1
}
]
}
}

Chaos Types

Network Chaos

TypeDescriptionConfiguration
latencyAdd delays to responsesmin_latency_ms, max_latency_ms, jitter_ms
bandwidthLimit data transfer rate(use slow_drip instead)
packet_lossDrop random requests entirelyprobability
disconnectDrop connection mid-responsedrop_after_percent, drop_after_bytes
slow_dripTrickle bytes slowlybytes_per_ms, chunk_size
timeoutNever respond (simulate timeout)probability

Response Timing

TypeDescriptionConfiguration
out_of_orderDeliver responses in random orderreorder_min_requests, reorder_max_wait_ms
staleVery long delays (hours)stale_delay_ms

HTTP Errors

TypeDescriptionConfiguration
http_errorInject HTTP error codeserror_codes[], error_message
rate_limitSimulate 429 rate limiting(use http_error with code 429)

Data Corruption

TypeDescriptionConfiguration
truncateCut off response bodytruncate_percent (0.0-1.0, portion to keep)
bit_flipRandom byte changes(advanced)
corrupt_jsonMalform JSON responses(advanced)

Rule Configuration

Matching Criteria

{
"url_pattern": "/api/users.*", // Regex pattern for URLs
"methods": ["POST", "PUT"], // HTTP methods (empty = all)
"probability": 0.3 // 0.0-1.0, chance of applying
}

Latency Configuration

{
"type": "latency",
"min_latency_ms": 100, // Minimum delay
"max_latency_ms": 2000, // Maximum delay
"jitter_ms": 500 // Random +/- jitter
}

Slow-Drip (Bandwidth Throttling)

{
"type": "slow_drip",
"bytes_per_ms": 5, // 5 bytes/ms = 5KB/s
"chunk_size": 10 // Write 10 bytes at a time
}

Connection Drops

{
"type": "disconnect",
"drop_after_percent": 0.5, // Drop after 50% of body sent
// OR
"drop_after_bytes": 1024 // Drop after 1KB sent
}

Response Reordering

{
"type": "out_of_order",
"reorder_min_requests": 3, // Batch this many before shuffling
"reorder_max_wait_ms": 500 // Max time to wait for batch
}

Managing Chaos

Check Status

proxy {action: "chaos", id: "app", chaos_operation: "status"}

Returns:

{
"enabled": true,
"preset": "flaky-api",
"stats": {
"total_requests": 142,
"affected_count": 38,
"latency_injected_ms": 45000,
"errors_injected": 7,
"drops_injected": 2
}
}

For the injection counters on their own, use chaos_operation: "stats".

Enable / Disable Chaos

Toggle chaos off without discarding your configured rules, then back on later:

proxy {action: "chaos", id: "app", chaos_operation: "disable"}
proxy {action: "chaos", id: "app", chaos_operation: "enable"}

Clear All Rules

proxy {action: "chaos", id: "app", chaos_operation: "clear"}

Add or Remove Individual Rules

Add one rule with add_rule (the rest of the configuration stays intact):

proxy {
action: "chaos",
id: "app",
chaos_operation: "add_rule",
chaos_rule: {
"id": "api-latency",
"type": "latency",
"enabled": true,
"url_pattern": "/api/.*",
"min_latency_ms": 500,
"max_latency_ms": 3000,
"probability": 0.5
}
}

Remove one rule by its id:

proxy {action: "chaos", id: "app", chaos_operation: "remove_rule", chaos_rule_id: "api-latency"}

Real-World Testing Scenarios

Testing Loading States

# Add 2-5 second delay to all API calls
proxy {
action: "chaos",
id: "app",
chaos_operation: "set",
chaos_config: {
"enabled": true,
"rules": [{
"id": "slow-everything",
"type": "latency",
"enabled": true,
"min_latency_ms": 2000,
"max_latency_ms": 5000,
"probability": 1.0
}]
}
}

Now verify: Do your loading spinners appear? Is the UI responsive during loads?

Testing Error Handling

# 50% of API calls fail with server errors
proxy {
action: "chaos",
id: "app",
chaos_operation: "set",
chaos_config: {
"enabled": true,
"rules": [{
"id": "server-errors",
"type": "http_error",
"enabled": true,
"error_codes": [500, 502, 503],
"probability": 0.5
}]
}
}

Now verify: Do error messages appear? Can users retry? Does state stay consistent?

Testing Race Conditions

# Responses arrive in random order
proxy {action: "chaos", id: "app", chaos_operation: "preset", chaos_preset: "race-condition"}

Now verify: If user types in search and gets responses out of order, does the UI show stale results?

Testing Offline Behavior

# Drop 100% of requests
proxy {
action: "chaos",
id: "app",
chaos_operation: "set",
chaos_config: {
"enabled": true,
"rules": [{
"id": "offline",
"type": "packet_loss",
"enabled": true,
"probability": 1.0
}]
}
}

Now verify: Does offline detection trigger? Are there helpful error messages?

Testing Token Expiry

# Return 401 for all API calls
proxy {
action: "chaos",
id: "app",
chaos_operation: "set",
chaos_config: {
"enabled": true,
"rules": [{
"id": "expire-tokens",
"type": "http_error",
"enabled": true,
"url_pattern": "/api/.*",
"error_codes": [401],
"probability": 1.0
}]
}
}

Now verify: Does the app redirect to login? Is state preserved for after re-auth?

Reproducible Chaos

For consistent test runs, set a random seed inside chaos_config:

proxy {
action: "chaos",
id: "app",
chaos_operation: "set",
chaos_config: {
"enabled": true,
"seed": 12345,
"rules": [ /* ... */ ]
}
}

Same seed = same sequence of chaos decisions.

Integration with Testing

Manual Exploratory Testing

  1. Start your dev server and proxy
  2. Apply a chaos preset
  3. Click through your app
  4. Watch for broken states, missing loaders, poor error messages

Automated E2E Tests

Apply chaos through the agnt MCP tool before driving your E2E suite, then assert that your error and retry UI behaves:

# Before the test run, enable a flaky-API preset on the proxy
proxy {action: "chaos", id: "app", chaos_operation: "preset", chaos_preset: "flaky-api"}
// In Playwright/Cypress, point the browser at the proxy URL and assert resilience
test('handles API errors gracefully', async ({ page }) => {
await page.goto('/dashboard');
// Error states should be visible due to injected chaos
await expect(page.locator('.error-message')).toBeVisible();
await expect(page.locator('.retry-button')).toBeEnabled();
});

When the run finishes, clear chaos with proxy {action: "chaos", id: "app", chaos_operation: "clear"}.

Best Practices

  1. Start with presets - They cover common scenarios well
  2. Test one thing at a time - Don't combine too many chaos types
  3. Document expected behavior - What SHOULD happen under chaos?
  4. Add chaos to CI - Run a subset of E2E tests with chaos enabled
  5. Use probability < 1.0 - Intermittent failures are harder to handle than consistent ones

See Also