Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

runtime: wasm: fatal error: all goroutines are asleep - deadlock! #41310

darkLord19 opened this issue Sep 10, 2020 · 10 comments

runtime: wasm: fatal error: all goroutines are asleep - deadlock! #41310

darkLord19 opened this issue Sep 10, 2020 · 10 comments


Copy link

@darkLord19 darkLord19 commented Sep 10, 2020

What version of Go are you using (go version)?

$ go version
go version go1.15 darwin/amd64

Does this issue reproduce with the latest release?

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GOENV="/Users/uparmar/Library/Application Support/go/env"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/l7/x5hcvngd5m99k62kxv7srrdr0000gn/T/go-build448055941=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

Call Go function from js(from chrome dev tools console)

// +build js, wasm

package main

import (

func test(this js.Value, args []js.Value) interface{} {
	println("test called")
	resp, err := http.Get("")
	if err != nil {
		return nil
	defer resp.Body.Close()
	var tmp []byte
	println("response is", tmp)
	return nil

func registerFuncs() {
	js.Global().Set("test", js.FuncOf(test))

func main() {
	c := make(chan struct{})
	println("WASM Go Initialized")

<!doctype html>

	<meta charset="utf-8">

	<h1> TEST </h1>

	<script type="text/javascript" src="./wasm_exec.js"></script>
		if (!WebAssembly.instantiateStreaming) { // polyfill
			WebAssembly.instantiateStreaming = async (resp, importObject) => {
				const source = await (await resp).arrayBuffer();
				return await WebAssembly.instantiate(source, importObject);

		const go = new Go();
		let mod, inst;
		WebAssembly.instantiateStreaming(fetch("test.wasm"), go.importObject).then(
		async result => {
			mod = result.module;
			inst = result.instance;
		}).catch((err) => {



What did you expect to see?

Function running succesfully

What did you see instead?

wasm_exec.js:50 test called
wasm_exec.js:50 fatal error: all goroutines are asleep - deadlock!
wasm_exec.js:50 goroutine 1 [chan receive]:
wasm_exec.js:50 main.main()
wasm_exec.js:50 	/Users/uparmar/Development/test_wasm/test.go:33 +0x7
wasm_exec.js:50 goroutine 6 [select]:
wasm_exec.js:50 net/http.(*Transport).RoundTrip(0x33c860, 0x486000, 0x33c860, 0x0, 0x0)
wasm_exec.js:50 	/usr/local/Cellar/go/1.15/libexec/src/net/http/roundtrip_js.go:168 +0x55
wasm_exec.js:50 net/http.send(0x486000, 0xc8260, 0x33c860, 0x0, 0x0, 0x0, 0x0, 0x40c040, 0x424ab0, 0x1)
wasm_exec.js:50 	/usr/local/Cellar/go/1.15/libexec/src/net/http/client.go:252 +0x5b
wasm_exec.js:50 net/http.(*Client).send(0x3490c0, 0x486000, 0x0, 0x0, 0x0, 0x40c040, 0x0, 0x1, 0xf8)
wasm_exec.js:50 	/usr/local/Cellar/go/1.15/libexec/src/net/http/client.go:176 +0x13
wasm_exec.js:50 net/http.(*Client).do(0x3490c0, 0x486000, 0x0, 0x0, 0x0)
wasm_exec.js:50 	/usr/local/Cellar/go/1.15/libexec/src/net/http/client.go:718 +0x38
wasm_exec.js:50 net/http.(*Client).Do(...)
wasm_exec.js:50 	/usr/local/Cellar/go/1.15/libexec/src/net/http/client.go:586
wasm_exec.js:50 net/http.(*Client).Get(0x3490c0, 0x8c00d, 0x38, 0x41a138, 0x14a30008, 0x14a80007)
wasm_exec.js:50 	/usr/local/Cellar/go/1.15/libexec/src/net/http/client.go:475 +0xe
wasm_exec.js:50 net/http.Get(...)
wasm_exec.js:50 	/usr/local/Cellar/go/1.15/libexec/src/net/http/client.go:447
wasm_exec.js:50 main.test(0x0, 0x0, 0x368070, 0x0, 0x0, 0x0, 0x0)
wasm_exec.js:50 	/Users/uparmar/Development/test_wasm/test.go:12 +0x7
wasm_exec.js:50 syscall/js.handleEvent()
wasm_exec.js:50 	/usr/local/Cellar/go/1.15/libexec/src/syscall/js/func.go:96 +0x24
wasm_exec.js:141 exit code: 2
Copy link

@agnivade agnivade commented Sep 10, 2020

This is documented here:

As a consequence, if one wrapped function blocks, JavaScript's event loop is blocked until that function returns. Hence, calling any async JavaScript API, which requires the event loop, like fetch (http.Client), will cause an immediate deadlock. Therefore a blocking function should explicitly start a new goroutine.

You would need to perform the http.Get in a separate goroutine.

Copy link
Contributor Author

@darkLord19 darkLord19 commented Sep 10, 2020

Thanks @agnivade for clarification.

Copy link
Contributor Author

@darkLord19 darkLord19 commented Sep 12, 2020

@agnivade I am doing http.Get in seperate routine but still getting error. I am creating a channel before invoking go routine to perform http.Get and writing on it from inside the go routine and reading from channel later down the function. Is this also a limitation?

Copy link

@agnivade agnivade commented Sep 13, 2020

Yes, that does not change anything if you read from the channel in the same function. It will still cause the deadlock. You have to create a separate event handler loop or some other mechanism to make http requests and read responses.

Copy link

@happybeing happybeing commented Nov 11, 2020

I'm new to Go so would appreciate any thoughts on my scenario. I'm not doing the GET directly, but have compiled go-git to wasm and am calling its repo.Clone() in the browser, which causes the same error as in the OP. The http requests and responses are there and look ok, but Go has died with the error fatal error: all goroutines are asleep - deadlock! wasm_exec.js.

So my question is about ways to solve this when not doing the fetches directly, but when they are happening in another package (which would not expect to be used in the browser). Can I solve this without re-writing the relevant parts of go-git?

If not I guess I'll look into modifying go-git to use my own "fetch" package ("in another event loop" whatever that means 😄), but am wondering if you can offer any advice to someone new to Go. Thanks.

Copy link
Contributor Author

@darkLord19 darkLord19 commented Nov 11, 2020

You can try wrapping repo.Clone() call in a seperate go routine.

Copy link

@happybeing happybeing commented Nov 11, 2020

Thanks. I'm trying the following, but maybe it can only work if Clone() only does a single fetch(), because I'm seeing the Clone() get further (more console output) but still fail with the same error.

This is in a funciton called via the wasm go bridge so I can't put it in a playground:

	url = ""
	fs := memfs.New()
	storage := memory.NewStorage()
	var wg sync.WaitGroup
	go func() {
		_, err := git.Clone(storage, fs, &git.CloneOptions{ URL: url })
		if err != nil {
			println("git.Clone() failed: ", err.Error())


Or might the problem remain because this is (I think) a nested goroutine?

Copy link

@happybeing happybeing commented Nov 11, 2020

I think I have solved this, the issue remained because I was waiting on the result so if I remove the WaitGroup altogether and just create the goroutine as it, it doesn't exit as in:

	url = ""
	fs := memfs.New()
	storage := memory.NewStorage()
	go func() {
		_, err := git.Clone(storage, fs, &git.CloneOptions{URL: url})
		if err != nil {
			println("git.Clone() failed: ", err.Error())

It doesn't clone the repo, but that's a different issue. Thanks for your help.

Copy link
Contributor Author

@darkLord19 darkLord19 commented Nov 12, 2020

You're cloning in memory but not using it's return value so that might be issue. Try doing

repo, err := repo.Clone(storage, fs,...)

And this repo variable will contain cloned repo.

And again you won't be able to wait for go routine
You can try using js promises and see if it works

func Clone() js.Func {
	return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
		// Handler for the Promise: this is a JS function
		// It receives two arguments, which are JS functions themselves: resolve and reject
		handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
			resolve := args[0]
			// Commented out because this Promise never fails
			//reject := args[1]

			// Now that we have a way to return the response to JS, spawn a goroutine
			// This way, we don't block the event loop and avoid a deadlock
			go func() {
				r, err := repo.Clone()
				// Resolve the Promise, passing anything back to JavaScript
				// This is done by invoking the "resolve" function passed to the handler

			// The handler of a Promise doesn't return any value
			return nil

		// Create and return the Promise object
		promiseConstructor := js.Global().Get("Promise")
		return promiseConstructor.New(handler)

To invoke from JavaScript

async function MyFunc() {
    // Get the Promise from Go
    const p = Clone()
    // Await for the Promise to resolve
    const repo = await p
    // Use repo response

Copy link

@happybeing happybeing commented Nov 12, 2020

I'm pretty sure the remaining issue is because I'm doing this in the browser, so the server issues a 404 (after initially responding as desired), so it isn't a concern for this thread. It's also not vital for my use case, more for testing at least for now so I'm happy!

And thanks for your help. Go has a great community and I've had excellent help whenever I get stuck.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
None yet

No branches or pull requests

4 participants