<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/goroutine-leak-profiles-vjrziolul" -->

---
title: Goroutine Leak Profiles | daily.dev
description: Go 1.27 introduces a new goroutine leak profiler built into runtime/pprof and net/http/pprof, available at the /debug/pprof/goroutineleak endpoint. Unlike...
canonical: https://daily.dev/posts/goroutine-leak-profiles-vjrziolul
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Goroutine Leak Profiles | daily.dev
og:description: Go 1.27 introduces a new goroutine leak profiler built into runtime/pprof and net/http/pprof, available at the /debug/pprof/goroutineleak endpoint. Unlike...
og:url: https://daily.dev/posts/goroutine-leak-profiles-vjrziolul
og:image: https://api.daily.dev/og/posts/vjrzIOLul.png
og:image:alt: Goroutine Leak Profiles
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.

# Goroutine Leak Profiles

**[Go](https://daily.dev/sources/golang)** · 26 min read · 0 upvotes · 0 comments

## Summary

Go 1.27 introduces a new goroutine leak profiler built into runtime/pprof and net/http/pprof, available at the /debug/pprof/goroutineleak endpoint. Unlike regular goroutine profiles, it precisely identifies goroutines permanently blocked on channels or sync package primitives (Mutex, RWMutex, WaitGroup, Cond) with little to no false positives, by adapting the garbage collector's mark-and-sweep algorithm to trace liveness through goroutine references. The piece walks through detecting and fixing common leak patterns (unbuffered channel sends on early return, double sends, missing unlock, range over unclosed channels, mutual blocking between channels and mutexes) using real-world examples from CockroachDB, etcd, Kubernetes, and Moby. It also covers the implementation's core liveness algorithm, limitations (memory overreach, non-standard blocking sources like file/network IO are undetectable, non-determinism), and performance costs such as the O(n^2) worst case in daisy-chain scenarios.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://go.dev/blog/goroutine-leak-profiles>

## Questions this post answers

### What does the new goroutine leak profiler in Go 1.27 detect?

Go 1.27 adds a goroutine leak profiler, accessible via runtime/pprof as the goroutineleak profile type or through the /debug/pprof/goroutineleak endpoint when net/http/pprof is set up. It precisely identifies goroutines permanently blocked on channel operations or sync package primitives (Mutex, RWMutex, WaitGroup, Cond), generating little to no false positives, though it cannot detect leaks from file IO, network IO, or direct system calls.

_Track new Go runtime tooling like this on daily.dev as you plan upgrades and debug production concurrency issues._

### How does the Go goroutine leak profiler determine that a goroutine is leaked?

It adapts the existing tri-color mark-and-sweep garbage collector: instead of marking all goroutines as reachable roots, it marks only unblocked goroutines as roots, traces memory they reference, then adds any goroutines blocked on marked primitives as roots too, repeating until no more live goroutines are found. Anything left unmarked afterward is reported as leaked, based on a formal liveness definition.

_Developers debugging concurrency bugs can follow how Go's runtime internals evolve via daily.dev._

### How do I fix a goroutine leak caused by an unbuffered channel send when a receiver returns early on error?

Give the channel a buffer sized to the number of sender goroutines, for example ch := make(chan result, len(ws)), so that senders can complete their send without blocking even if the receiving loop exits early due to an error. This pattern, seen in real Uber production code, causes remaining worker goroutines to block forever on an unbuffered channel once the receiver returns.

_Save this fix for the next time an early return leaves goroutines blocked in your own Go services on daily.dev._

## Similar posts on daily.dev

- [The Goroutines That Refuse to Die — and How Go 1.26 Finally Made Them Visible](https://daily.dev/posts/the-goroutines-that-refuse-to-die-and-how-go-1-26-finally-made-them-visible-d8q8hxxfn) · Medium · 2 upvotes · 0 comments
- [Detecting goroutine leaks in modern Go](https://daily.dev/posts/detecting-goroutine-leaks-in-modern-go-luv5ga3m1) · Anton Zhiyanov · 24 upvotes · 1 comments

---

Tags: [#golang](https://daily.dev/tags/golang)

[View this post on daily.dev](https://daily.dev/posts/goroutine-leak-profiles-vjrziolul)

```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":"Goroutine Leak Profiles","url":"https://daily.dev/posts/goroutine-leak-profiles-vjrziolul","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/goroutine-leak-profiles-vjrziolul"},"datePublished":"2026-09-02T18:11:20.907Z","dateModified":"2026-09-02T19:41:56.772Z","description":"Go 1.27 introduces a new goroutine leak profiler built into runtime/pprof and net/http/pprof, available at the /debug/pprof/goroutineleak endpoint. Unlike...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/39b837b91d435ac673a1daa7b32a05fa?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/39b837b91d435ac673a1daa7b32a05fa?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Go","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":"Go","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/f416aeab0a1f4b9faa22d93768c97905","url":"https://daily.dev/sources/golang"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/goroutine-leak-profiles-vjrziolul","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"golang","timeRequired":"PT26M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Go","item":"https://daily.dev/sources/golang"},{"@type":"ListItem","position":3,"name":"Goroutine Leak Profiles"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/goroutine-leak-profiles-vjrziolul#faq","mainEntity":[{"@type":"Question","name":"What does the new goroutine leak profiler in Go 1.27 detect?","acceptedAnswer":{"@type":"Answer","text":"Go 1.27 adds a goroutine leak profiler, accessible via runtime/pprof as the goroutineleak profile type or through the /debug/pprof/goroutineleak endpoint when net/http/pprof is set up. It precisely identifies goroutines permanently blocked on channel operations or sync package primitives (Mutex, RWMutex, WaitGroup, Cond), generating little to no false positives, though it cannot detect leaks from file IO, network IO, or direct system calls. Track new Go runtime tooling like this on daily.dev as you plan upgrades and debug production concurrency issues."}},{"@type":"Question","name":"How does the Go goroutine leak profiler determine that a goroutine is leaked?","acceptedAnswer":{"@type":"Answer","text":"It adapts the existing tri-color mark-and-sweep garbage collector: instead of marking all goroutines as reachable roots, it marks only unblocked goroutines as roots, traces memory they reference, then adds any goroutines blocked on marked primitives as roots too, repeating until no more live goroutines are found. Anything left unmarked afterward is reported as leaked, based on a formal liveness definition. Developers debugging concurrency bugs can follow how Go's runtime internals evolve via daily.dev."}},{"@type":"Question","name":"How do I fix a goroutine leak caused by an unbuffered channel send when a receiver returns early on error?","acceptedAnswer":{"@type":"Answer","text":"Give the channel a buffer sized to the number of sender goroutines, for example ch := make(chan result, len(ws)), so that senders can complete their send without blocking even if the receiving loop exits early due to an error. This pattern, seen in real Uber production code, causes remaining worker goroutines to block forever on an unbuffered channel once the receiver returns. Save this fix for the next time an early return leaves goroutines blocked in your own Go services on daily.dev."}}]}
```

