<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/the-goroutines-that-refuse-to-die-and-how-go-1-26-finally-made-them-visible-d8q8hxxfn" -->

---
title: The Goroutines That Refuse to Die — and How Go 1.26...
description: Go 1.26 introduces an experimental `goroutineleak` pprof profile that uses the garbage collector's reachability analysis to detect leaked goroutines — those...
canonical: https://daily.dev/posts/the-goroutines-that-refuse-to-die-and-how-go-1-26-finally-made-them-visible-d8q8hxxfn
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: The Goroutines That Refuse to Die — and How Go 1.26 Finally Made Them Visible | daily.dev
og:description: Go 1.26 introduces an experimental `goroutineleak` pprof profile that uses the garbage collector's reachability analysis to detect leaked goroutines — those...
og:url: https://daily.dev/posts/the-goroutines-that-refuse-to-die-and-how-go-1-26-finally-made-them-visible-d8q8hxxfn
og:image: https://api.daily.dev/og/posts/D8Q8hxXfN.png
og:image:alt: The Goroutines That Refuse to Die — and How Go 1.26 Finally Made Them Visible
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# The Goroutines That Refuse to Die — and How Go 1.26 Finally Made Them Visible

**[Medium](https://daily.dev/sources/medium_js)** · 9 min read · 2 upvotes · 0 comments

## Summary

Go 1.26 introduces an experimental `goroutineleak` pprof profile that uses the garbage collector's reachability analysis to detect leaked goroutines — those blocked on synchronization primitives whose underlying objects have become unreachable. The post walks through a concrete example: two leak functions that create unbuffered channels, spawn goroutines that block on them, then let the channels go out of scope. After a 5-second sleep to allow GC cycles, `pprof.Lookup("goroutineleak")` returns a profile showing exactly how many goroutines are stuck, the file and line where each is blocked, and which code path spawned them. The profile is enabled via `GOEXPERIMENT=goroutineleakprofile` and works by piggybacking on the Green Tea GC's existing heap trace — no extra bookkeeping required. Key limitations: it only catches leaks on unreachable primitives (not globals), and the API may change before the feature graduates from experimental status.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://towardsdev.com/the-goroutines-that-refuse-to-die-and-how-go-1-26-finally-made-them-visible-52da51317c5b>

## Questions this post answers

### How do I detect leaked goroutines in Go 1.26 using the new goroutineleak profile?

Enable the experimental profile with GOEXPERIMENT=goroutineleakprofile, then call pprof.Lookup("goroutineleak") and WriteTo(os.Stdout, 1) after giving the runtime time for a GC cycle. The profile scans goroutines blocked on channels, mutexes, or WaitGroups, checks whether the primitive they wait on is unreachable, and prints symbolized stack traces with counts and file:line locations for each leak.

_Developers chasing memory creep from stuck goroutines can track Go runtime tooling like this via daily.dev._

### What are the limitations of Go 1.26's experimental goroutineleak profiler?

It only detects leaks where the blocked-on primitive is unreachable from anywhere except the stuck goroutine itself, so a channel stored in a global variable stays invisible even if nothing ever signals it. It is also experimental, meaning the GOEXPERIMENT flag and pprof.Lookup name may change before the feature graduates, so it should be treated as a local diagnostic rather than a CI gate.

_Teams evaluating whether to adopt experimental Go diagnostics can follow tooling maturity updates on daily.dev._

### Why doesn't a goroutine blocked forever on an unclosed channel crash or show up as an obvious bug in Go?

A goroutine blocked on a channel that nobody will ever send to or close simply parks indefinitely without panicking or crashing; it just holds memory silently. Historically this required guessing which goroutine was stuck by watching pprof's goroutine count rise, since there was no way to see which specific stack was orphaned versus merely busy.

_Engineers debugging silent memory growth from concurrency bugs can keep up with Go tooling changes on daily.dev._

## Similar posts on daily.dev

- [Detecting goroutine leaks in modern Go](https://daily.dev/posts/detecting-goroutine-leaks-in-modern-go-luv5ga3m1) · Anton Zhiyanov · 24 upvotes · 1 comments
- [A Practical Guide to Profiling in Go](https://daily.dev/posts/a-practical-guide-to-profiling-in-go-fvzkksvyy) · JetBrains · 16 upvotes · 1 comments
- [Part 1: pprof Quick Start - Get Profiling in 10 Minutes](https://daily.dev/posts/part-1-pprof-quick-start---get-profiling-in-10-minutes-60bzn1m6e) · Developer 2.0 · 0 upvotes · 0 comments

---

Tags: [#general-programming](https://daily.dev/tags/general-programming), [#golang](https://daily.dev/tags/golang)

[View this post on daily.dev](https://daily.dev/posts/the-goroutines-that-refuse-to-die-and-how-go-1-26-finally-made-them-visible-d8q8hxxfn)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"The Goroutines That Refuse to Die — and How Go 1.26 Finally Made Them Visible","url":"https://daily.dev/posts/the-goroutines-that-refuse-to-die-and-how-go-1-26-finally-made-them-visible-d8q8hxxfn","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/the-goroutines-that-refuse-to-die-and-how-go-1-26-finally-made-them-visible-d8q8hxxfn"},"datePublished":"2026-07-27T19:36:23.611Z","dateModified":"2026-09-13T21:43:33.802Z","description":"Go 1.26 introduces an experimental `goroutineleak` pprof profile that uses the garbage collector's reachability analysis to detect leaked goroutines — those...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/9f832cf0dab9e708ad60a242a3bd88bc?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/9f832cf0dab9e708ad60a242a3bd88bc?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Medium","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Medium","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/medium","url":"https://daily.dev/sources/medium_js"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/the-goroutines-that-refuse-to-die-and-how-go-1-26-finally-made-them-visible-d8q8hxxfn","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":2},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"general-programming,golang","timeRequired":"PT9M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Medium","item":"https://daily.dev/sources/medium_js"},{"@type":"ListItem","position":3,"name":"The Goroutines That Refuse to Die — and How Go 1.26 Finally Made Them Visible"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/the-goroutines-that-refuse-to-die-and-how-go-1-26-finally-made-them-visible-d8q8hxxfn#faq","mainEntity":[{"@type":"Question","name":"How do I detect leaked goroutines in Go 1.26 using the new goroutineleak profile?","acceptedAnswer":{"@type":"Answer","text":"Enable the experimental profile with GOEXPERIMENT=goroutineleakprofile, then call pprof.Lookup(\"goroutineleak\") and WriteTo(os.Stdout, 1) after giving the runtime time for a GC cycle. The profile scans goroutines blocked on channels, mutexes, or WaitGroups, checks whether the primitive they wait on is unreachable, and prints symbolized stack traces with counts and file:line locations for each leak. Developers chasing memory creep from stuck goroutines can track Go runtime tooling like this via daily.dev."}},{"@type":"Question","name":"What are the limitations of Go 1.26's experimental goroutineleak profiler?","acceptedAnswer":{"@type":"Answer","text":"It only detects leaks where the blocked-on primitive is unreachable from anywhere except the stuck goroutine itself, so a channel stored in a global variable stays invisible even if nothing ever signals it. It is also experimental, meaning the GOEXPERIMENT flag and pprof.Lookup name may change before the feature graduates, so it should be treated as a local diagnostic rather than a CI gate. Teams evaluating whether to adopt experimental Go diagnostics can follow tooling maturity updates on daily.dev."}},{"@type":"Question","name":"Why doesn't a goroutine blocked forever on an unclosed channel crash or show up as an obvious bug in Go?","acceptedAnswer":{"@type":"Answer","text":"A goroutine blocked on a channel that nobody will ever send to or close simply parks indefinitely without panicking or crashing; it just holds memory silently. Historically this required guessing which goroutine was stuck by watching pprof's goroutine count rise, since there was no way to see which specific stack was orphaned versus merely busy. Engineers debugging silent memory growth from concurrency bugs can keep up with Go tooling changes on daily.dev."}}]}
```

