Skip to content

Instantly share code, notes, and snippets.

@hardikm9850
Last active February 5, 2026 05:05
Show Gist options
  • Select an option

  • Save hardikm9850/a63af8a86d74f1510be7033cf733af02 to your computer and use it in GitHub Desktop.

Select an option

Save hardikm9850/a63af8a86d74f1510be7033cf733af02 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SupervisorJob Exception Propagation</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
border-radius: 12px;
padding: 30px;
margin-bottom: 30px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
h1 {
color: #2c3e50;
border-bottom: 3px solid #3498db;
padding-bottom: 10px;
}
h2 {
color: #34495e;
margin-top: 30px;
}
.scenario {
margin: 20px 0;
}
.diagram {
display: flex;
flex-direction: column;
gap: 20px;
margin: 20px 0;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
}
.scope {
border: 3px solid #9b59b6;
border-radius: 8px;
padding: 20px;
background: #f4ecf7;
position: relative;
}
.scope-label {
position: absolute;
top: -12px;
left: 10px;
background: #9b59b6;
color: white;
padding: 2px 10px;
border-radius: 4px;
font-size: 14px;
font-weight: bold;
}
.children-container {
display: flex;
gap: 20px;
margin-top: 10px;
flex-wrap: wrap;
}
.coroutine {
border: 2px solid #3498db;
border-radius: 6px;
padding: 15px;
background: #ebf5fb;
flex: 1;
min-width: 200px;
position: relative;
}
.coroutine.failed {
border-color: #e74c3c;
background: #fadbd8;
}
.coroutine.cancelled {
border-color: #95a5a6;
background: #ecf0f1;
opacity: 0.7;
}
.coroutine.success {
border-color: #27ae60;
background: #d5f4e6;
}
.coroutine-label {
font-weight: bold;
margin-bottom: 8px;
color: #2c3e50;
}
.coroutine.failed .coroutine-label {
color: #c0392b;
}
.coroutine.cancelled .coroutine-label {
color: #7f8c8d;
}
.coroutine.success .coroutine-label {
color: #1e8449;
}
.nested {
margin-top: 10px;
padding: 10px;
border-left: 3px solid #95a5a6;
background: white;
}
.icon {
display: inline-block;
margin-right: 5px;
}
.explosion {
font-size: 20px;
}
.check {
color: #27ae60;
font-size: 18px;
}
.cross {
color: #e74c3c;
font-size: 18px;
}
.arrow {
color: #e74c3c;
font-weight: bold;
margin: 5px 0;
}
.legend {
display: flex;
gap: 20px;
margin: 20px 0;
flex-wrap: wrap;
}
.legend-item {
display: flex;
align-items: center;
gap: 8px;
}
.legend-box {
width: 40px;
height: 30px;
border-radius: 4px;
border: 2px solid;
}
.barrier {
border: 3px dashed #9b59b6;
margin: 10px 0;
position: relative;
}
.barrier-label {
position: absolute;
top: -12px;
right: 10px;
background: #9b59b6;
color: white;
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
}
.explanation {
background: #fff9e6;
border-left: 4px solid #f39c12;
padding: 15px;
margin: 15px 0;
border-radius: 4px;
}
.code-ref {
background: #ecf0f1;
padding: 2px 6px;
border-radius: 3px;
font-family: monospace;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>πŸ” SupervisorJob Exception Propagation</h1>
<div class="legend">
<div class="legend-item">
<div class="legend-box" style="border-color: #e74c3c; background: #fadbd8;"></div>
<span>Failed (threw exception)</span>
</div>
<div class="legend-item">
<div class="legend-box" style="border-color: #95a5a6; background: #ecf0f1;"></div>
<span>Cancelled</span>
</div>
<div class="legend-item">
<div class="legend-box" style="border-color: #27ae60; background: #d5f4e6;"></div>
<span>Completed successfully</span>
</div>
<div class="legend-item">
<div class="legend-box" style="border-color: #9b59b6; background: #f4ecf7;"></div>
<span>SupervisorJob scope</span>
</div>
</div>
</div>
<div class="container">
<h2>βœ… Scenario 1: Direct Children (Exception Contained)</h2>
<p>When coroutines are <strong>direct children</strong> of a SupervisorJob scope, they are isolated from each other.</p>
<div class="diagram">
<div class="scope">
<div class="scope-label">CoroutineScope(SupervisorJob())</div>
<div class="barrier">
<div class="barrier-label">πŸ›‘οΈ Protection Barrier</div>
</div>
<div class="children-container">
<div class="coroutine failed">
<div class="coroutine-label"><span class="explosion">πŸ’₯</span> launch { ... }</div>
<div>Direct Child 1</div>
<div class="arrow">↓ Exception thrown</div>
<div style="color: #c0392b; font-weight: bold;">❌ This child fails</div>
</div>
<div class="coroutine success">
<div class="coroutine-label"><span class="check">βœ“</span> launch { ... }</div>
<div>Direct Child 2</div>
<div style="margin-top: 10px;">πŸ›‘οΈ Protected by SupervisorJob</div>
<div style="color: #1e8449; font-weight: bold;">βœ… Continues running!</div>
</div>
</div>
</div>
</div>
<div class="explanation">
<strong>πŸ’‘ Key Point:</strong> The exception in Child 1 does NOT propagate to Child 2 because the SupervisorJob creates a protective barrier between direct siblings.
</div>
<pre style="background: #2c3e50; color: #ecf0f1; padding: 15px; border-radius: 6px; overflow-x: auto;"><code>val scope = CoroutineScope(SupervisorJob())
scope.launch { throw Exception("Boom") } // ← Direct child
scope.launch { println("I survive!") } // ← Direct child (protected)</code></pre>
</div>
<div class="container">
<h2>❌ Scenario 2: Nested Children (Exception Propagates)</h2>
<p>When coroutines are <strong>nested</strong> (children of children), SupervisorJob does NOT protect them.</p>
<div class="diagram">
<div class="scope">
<div class="scope-label">CoroutineScope(SupervisorJob())</div>
<div class="barrier">
<div class="barrier-label">πŸ›‘οΈ Barrier only at this level</div>
</div>
<div class="children-container">
<div class="coroutine cancelled" style="flex: 1; min-width: 400px;">
<div class="coroutine-label">launch { ... } - Parent Coroutine</div>
<div style="margin-bottom: 10px;">Direct Child (gets cancelled when child fails)</div>
<div style="border-top: 2px dashed #95a5a6; padding-top: 10px; margin-top: 10px;">
<div style="font-weight: bold; margin-bottom: 10px; color: #7f8c8d;">Nested children (NO protection):</div>
<div class="nested">
<div class="coroutine failed" style="margin-bottom: 10px;">
<div class="coroutine-label"><span class="explosion">πŸ’₯</span> async { task1() }</div>
<div class="arrow">↓ Exception thrown</div>
<div class="arrow">↓ Propagates to parent launch</div>
<div style="color: #c0392b; font-weight: bold;">❌ Fails</div>
</div>
<div class="coroutine cancelled">
<div class="coroutine-label"><span class="cross">βœ—</span> async { task2() }</div>
<div class="arrow">↓ Parent gets cancelled</div>
<div style="color: #7f8c8d; font-weight: bold;">⚠️ Gets cancelled too!</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="explanation">
<strong>πŸ’‘ Key Point:</strong> SupervisorJob only protects <span class="code-ref">direct children</span>. When task1() fails, it cancels its parent <span class="code-ref">launch</span>, which then cancels task2(). The barrier doesn't extend to nested levels!
</div>
<pre style="background: #2c3e50; color: #ecf0f1; padding: 15px; border-radius: 6px; overflow-x: auto;"><code>val scope = CoroutineScope(SupervisorJob())
scope.launch { // ← Direct child
val t1 = async { throw Exception() } // ← Nested (NOT protected)
val t2 = async { ... } // ← Nested (gets cancelled)
t1.await() // Exception propagates up to launch
}</code></pre>
</div>
<div class="container">
<h2>βœ… Scenario 3: Your Original Code FIXED</h2>
<p>Using <span class="code-ref">supervisorScope</span> creates a new protection barrier at the nested level.</p>
<div class="diagram">
<div class="scope">
<div class="scope-label">CoroutineScope(SupervisorJob())</div>
<div class="barrier">
<div class="barrier-label">πŸ›‘οΈ Outer Barrier</div>
</div>
<div class="children-container">
<div class="coroutine success" style="flex: 1; min-width: 400px;">
<div class="coroutine-label">launch { ... } - Parent</div>
<div style="margin-bottom: 10px;">Direct Child</div>
<div style="border: 3px solid #9b59b6; border-radius: 6px; padding: 10px; margin-top: 10px; background: #f4ecf7;">
<div style="font-weight: bold; margin-bottom: 10px; color: #8e44ad;">supervisorScope { ... }</div>
<div class="barrier" style="border-color: #8e44ad;">
<div class="barrier-label" style="background: #8e44ad;">πŸ›‘οΈ Inner Barrier</div>
</div>
<div style="display: flex; gap: 10px; margin-top: 15px;">
<div class="coroutine failed" style="flex: 1;">
<div class="coroutine-label"><span class="explosion">πŸ’₯</span> async { task1() }</div>
<div style="color: #c0392b; font-weight: bold; margin-top: 5px;">❌ Fails</div>
<div style="font-size: 12px; color: #7f8c8d;">Isolated by supervisorScope</div>
</div>
<div class="coroutine success" style="flex: 1;">
<div class="coroutine-label"><span class="check">βœ“</span> async { task2() }</div>
<div style="color: #1e8449; font-weight: bold; margin-top: 5px;">βœ… Survives!</div>
<div style="font-size: 12px; color: #1e8449;">Protected by barrier</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="explanation">
<strong>πŸ’‘ Solution:</strong> By wrapping the <span class="code-ref">async</span> calls in a <span class="code-ref">supervisorScope</span>, you create a new protection barrier. Now task1 and task2 are siblings at the same level, so they don't cancel each other!
</div>
<pre style="background: #2c3e50; color: #ecf0f1; padding: 15px; border-radius: 6px; overflow-x: auto;"><code>scope.launch {
supervisorScope { // ← Creates new barrier here!
val t1 = async { throw Exception() } // Protected
val t2 = async { ... } // Protected
val r1 = runCatching { t1.await() }
val r2 = runCatching { t2.await() } // Still completes!
}
}</code></pre>
</div>
<div class="container">
<h2>πŸ“Š Summary: Exception Propagation Rules</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin-top: 20px;">
<div style="border: 2px solid #3498db; padding: 20px; border-radius: 8px; background: #ebf5fb;">
<h3 style="margin-top: 0; color: #2980b9;">πŸ›‘οΈ SupervisorJob</h3>
<ul style="line-height: 1.8;">
<li>Only protects <strong>direct children</strong></li>
<li>Siblings don't cancel each other</li>
<li>Exceptions still propagate upward</li>
<li>One level deep only</li>
</ul>
</div>
<div style="border: 2px solid #9b59b6; padding: 20px; border-radius: 8px; background: #f4ecf7;">
<h3 style="margin-top: 0; color: #8e44ad;">πŸ”„ supervisorScope</h3>
<ul style="line-height: 1.8;">
<li>Creates barrier at any nesting level</li>
<li>Use inside coroutines</li>
<li>Protects sibling coroutines</li>
<li>Suspending function</li>
</ul>
</div>
<div style="border: 2px solid #e67e22; padding: 20px; border-radius: 8px; background: #fef5e7;">
<h3 style="margin-top: 0; color: #d35400;">🎯 Best Practice</h3>
<ul style="line-height: 1.8;">
<li>Use <code style="background: white; padding: 2px 6px; border-radius: 3px;">runCatching</code> with await()</li>
<li>Handle failures explicitly</li>
<li>Don't rely on implicit supervision</li>
<li>Be clear about error handling</li>
</ul>
</div>
</div>
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment