Skip to content

cloudflare.waitUntil not working: Uncaught Error: Go program has already exited #177

@schrodienieur

Description

@schrodienieur

Go version: 1.24
workers version: v0.30.2

I tried to make discord bot using cloudflare workers. All the commands are working normally, except the one that needed a background task.

I'm using this code below for testing as in the fetch-event example, it works without time sleep or very small time sleep, but it gets error when time sleep is longer.

cloudflare.WaitUntil(func() {
	for i := 0; i < 5; i++ {
		time.Sleep(10 * time.Millisecond) // error when time.Sleep is added
		log.Println(i)
	}
})
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(InteractionResponse{
	Type: InteractionResponseDeferredChannelMessageWithSource,
	Data: &InteractionResponseData{
		Flags: MessageFlagsEphemeral,
	},
})

The error:

⎔ Starting local server...
2025/06/11 22:58:51 0
2025/06/11 22:58:51 1
[wrangler:info] POST /api/interactions 200 OK (402ms)
X [ERROR] Uncaught Error: Go program has already exited

      at _resume (file:///D:/Go/blegping-discord-worker-go/build/wasm_exec.js:565:11)
      at null.<anonymous> (file:///D:/Go/blegping-discord-worker-go/build/wasm_exec.js:285:14)   


X [ERROR] Uncaught (async) Error: Go program has already exited

      at globalThis.Go._resume
  (file:///D:/Go/blegping-discord-worker-go/.wrangler/tmp/dev-Zd86Z9/worker.js:580:15)
      at file:///D:/Go/blegping-discord-worker-go/.wrangler/tmp/dev-Zd86Z9/worker.js:332:22      


X [ERROR] Uncaught (async) Error: The Workers runtime canceled this request because it detected that your Worker's code had hung and would never generate a response. Refer to: https://developers.cloudflare.com/workers/observability/errors/

I also tried this code, get same error also

cloudflare.WaitUntil(func() {
	for i := 0; i < 5; i++ {
		time.Sleep(time.Second)
	}
	fmt.Println("5-second task completed")
})

Here's my route handler

//go:build js && wasm

package main

import (
	"crypto/ed25519"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"time"

	"net/http"

	"github.com/syumai/workers"
	"github.com/syumai/workers/cloudflare"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		discordPublicKey := cloudflare.Getenv("DISCORD_PUBLIC_KEY")
		if discordPublicKey == "" {
			http.Error(w, "DISCORD_PUBLIC_KEY is not set", http.StatusInternalServerError)
			return
		}
		msg := "Hello!"
		w.Write([]byte(msg))
	})

	http.HandleFunc("/api/interactions", func(w http.ResponseWriter, req *http.Request) {

		discordPublicKey := cloudflare.Getenv("DISCORD_PUBLIC_KEY")
		if discordPublicKey == "" {
			http.Error(w, "DISCORD_PUBLIC_KEY is not set", http.StatusInternalServerError)
			return
		}
		// log.Println("publickey Env", discordPublicKey)
		publicKey, err := hex.DecodeString(discordPublicKey)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		// log.Println("publickey", publicKey)

		if verified := VerifyInteraction(req, ed25519.PublicKey(publicKey)); !verified {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}

		// log.Println("Reads body")
		body, err := io.ReadAll(req.Body)
		if err != nil {
			http.Error(w, fmt.Sprintf("read body error: %s", err.Error()), http.StatusInternalServerError)
			return
		}
		// log.Println("Parse body")
		interaction := Interaction{}
		err = interaction.UnmarshalJSON(body)
		if err != nil {
			http.Error(w, fmt.Sprintf("parse body as interaction error: %s", err.Error()), http.StatusInternalServerError)
			return
		}

		switch interaction.Type {
		case InteractionPing:
			log.Println("Ping")
			w.Header().Set("Content-Type", "application/json")
			json.NewEncoder(w).Encode(InteractionResponse{
				Type: InteractionResponsePong,
			})
		case InteractionApplicationCommand:
			commandData := interaction.ApplicationCommandData()
			// token := interaction.Token
			// log.Println(commandData.Name)
			switch commandData.Name {
			case "background":

				// cloudflare.WaitUntil(func() {
				// 	time.Sleep(3 * time.Second)
				// 	res, err := DeferUpdate(cloudflare.Getenv("DISCORD_APPLICATION_ID"), token, "updated message", nil)
				// 	if err != nil {
				// 		log.Println(err.Error())
				// 	} else {
				// 		log.Println(res)
				// 	}
				// })

				cloudflare.WaitUntil(func() {
					for i := 0; i < 5; i++ {
						time.Sleep(10 * time.Millisecond)
						log.Println(i)
					}
				})

				w.Header().Set("Content-Type", "application/json")
				json.NewEncoder(w).Encode(InteractionResponse{
					Type: InteractionResponseDeferredChannelMessageWithSource,
					Data: &InteractionResponseData{
						Flags: MessageFlagsEphemeral,
					},
				})

			case "serverinfo":
				log.Println("serverinfo")
				w.Header().Set("Content-Type", "application/json")
				json.NewEncoder(w).Encode(InteractionResponse{
					Type: InteractionResponseChannelMessageWithSource,
					Data: &InteractionResponseData{
						Content: "Server Info",
					},
				})
			default:
				log.Println("unknown command")
				w.Header().Set("Content-Type", "application/json")
				json.NewEncoder(w).Encode(InteractionResponse{
					Type: InteractionResponseChannelMessageWithSource,
					Data: &InteractionResponseData{
						Content: fmt.Sprintf("Command %s handler not found", commandData.Name),
					},
				})
			}
		}

	})
	workers.Serve(nil) // use http.DefaultServeMux
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions