{"record":{"id":"71599a79adea723b","repo":"golang/go","slug":"internal-error-async-command-returned-a-nil-waitf","errorCode":null,"errorMessage":"internal error: async command returned a nil WaitFunc","messagePattern":"internal error: async command returned a nil WaitFunc","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/cmd/internal/script/engine.go","lineNumber":561,"sourceCode":"\t}\n\n\treturn true, nil\n}\n\nfunc (e *Engine) runCommand(s *State, cmd *command, impl Cmd) error {\n\tif impl == nil {\n\t\treturn cmdError(cmd, errors.New(\"unknown command\"))\n\t}\n\n\tasync := impl.Usage().Async\n\tif cmd.background && !async {\n\t\treturn cmdError(cmd, errors.New(\"command cannot be run in background\"))\n\t}\n\n\twait, runErr := impl.Run(s, cmd.args...)\n\tif wait == nil {\n\t\tif async && runErr == nil {\n\t\t\treturn cmdError(cmd, errors.New(\"internal error: async command returned a nil WaitFunc\"))\n\t\t}\n\t\treturn checkStatus(cmd, runErr)\n\t}\n\tif runErr != nil {\n\t\treturn cmdError(cmd, errors.New(\"internal error: command returned both an error and a WaitFunc\"))\n\t}\n\n\tif cmd.background {\n\t\ts.background = append(s.background, backgroundCmd{\n\t\t\tcommand: cmd,\n\t\t\twait:    wait,\n\t\t})\n\t\t// Clear stdout and stderr, since they no longer correspond to the last\n\t\t// command executed.\n\t\ts.stdout = \"\"\n\t\ts.stderr = \"\"\n\t\treturn nil\n\t}","sourceCodeStart":543,"sourceCodeEnd":579,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/internal/script/engine.go#L543-L579","documentation":"Internal error returned by Engine.runCommand when an async command's Run returns a nil WaitFunc AND a nil error. The script command contract requires that an async command (Usage.Async == true) return a non-nil WaitFunc so the engine can later collect its result; returning nil/nil is a contract violation by the command implementation, not a user script error.","triggerScenarios":"A registered async Cmd whose Run function returns (nil, nil). The engine's runCommand detects `wait == nil` while `async && runErr == nil` and surfaces this as an internal error to avoid a nil-pointer panic when it later calls wait(s).","commonSituations":"Author of a custom async script command forgets to return a WaitFunc on the success path, or returns an untyped nil in a variable typed as WaitFunc (Go nil-interface trap: `var w WaitFunc; return w, nil` is fine, but `return nil, nil` through a named return typed differently can yield a non-nil interface).","solutions":["In your async command's Run, always return a non-nil WaitFunc closure on the success path.","Watch for the typed-nil-interface pitfall: `var f WaitFunc; return f, nil` returns a nil interface only if WaitFunc is referenced as such; return an explicit closure to be safe.","Cover the success path with a test that asserts the returned WaitFunc is non-nil."],"exampleFix":"// before\nreturn func(s *script.State) (WaitFunc, error) {\n    // ... start goroutine ...\n    return nil, nil // async but no WaitFunc\n}\n\n// after\nreturn func(s *script.State) (WaitFunc, error) {\n    done := make(chan struct{})\n    go func() { /* ... */; close(done) }()\n    return func(s *script.State) (stdout, stderr string, err error) {\n        <-done\n        return out, errOut, nil\n    }, nil\n}","handlingStrategy":"validation","validationCode":"// In a custom async command, assert the WaitFunc is non-nil before returning.\n// (Inside the command's Run)\n//   if wait == nil { panic(\"async command returned nil WaitFunc\") }\n","typeGuard":"func isNilWaitFunc(err error) bool {\n    var ce *script.CommandError\n    return errors.As(err, &ce) && ce.Err != nil &&\n        strings.Contains(ce.Err.Error(), \"async command returned a nil WaitFunc\")\n}","tryCatchPattern":"// This is an internal/contract error; fix the offending command implementation, not the script.","preventionTips":["Always return a non-nil WaitFunc from async commands on the success path.","Beware the typed-nil-interface pitfall when returning a typed nil WaitFunc.","Unit-test that async commands return a non-nil WaitFunc."],"tags":["script","internal-error","async","command-contract","test-framework"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}