{"record":{"id":"5c01ce4a05fe7962","repo":"golang/go","slug":"internal-error-command-returned-both-an-error-and","errorCode":null,"errorMessage":"internal error: command returned both an error and a WaitFunc","messagePattern":"internal error: command returned both an error and a WaitFunc","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/cmd/internal/script/engine.go","lineNumber":566,"sourceCode":"func (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}\n\n\tstdout, stderr, waitErr := wait(s)\n\ts.stdout = stdout\n\ts.stderr = stderr\n\tif stdout != \"\" {","sourceCodeStart":548,"sourceCodeEnd":584,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/internal/script/engine.go#L548-L584","documentation":"Internal error returned by Engine.runCommand when a command's Run returns both a non-nil WaitFunc AND a non-nil error. The contract is exclusive: either Run returns an error (synchronous failure, wait == nil) or it returns a WaitFunc to be awaited later (runErr == nil). Returning both is ambiguous and treated as an implementation bug.","triggerScenarios":"A Cmd's Run function returns something like `return waitFn, fmt.Errorf(\"...\")` in the same call. The engine checks `runErr != nil` after confirming `wait != nil` and surfaces the internal error.","commonSituations":"A custom command author partially fails but still returns the closure, or refactors a sync command to async and leaves an error return on the success path. The typed-nil pitfall can also produce a non-nil error interface unexpectedly.","solutions":["Make Run return either (WaitFunc, nil) on success or (nil, error) on failure — never both non-nil.","On partial failure, either return (nil, err) immediately or capture the error inside the WaitFunc closure and return (waitFn, nil).","Add a unit test asserting the two return values are never simultaneously non-nil."],"exampleFix":"// before\nreturn func(s *script.State) (WaitFunc, error) {\n    if bad {\n        return waitFn, errors.New(\"bad\") // both non-nil\n    }\n    return waitFn, nil\n}\n\n// after\nreturn func(s *script.State) (WaitFunc, error) {\n    if bad {\n        return nil, errors.New(\"bad\")\n    }\n    return waitFn, nil\n}","handlingStrategy":"validation","validationCode":"// In a custom command, ensure Run never returns both non-nil.\n//   if wait != nil && err != nil { err = nil } // or: wait = nil\n","typeGuard":"func isBothWaitAndErr(err error) bool {\n    var ce *script.CommandError\n    return errors.As(err, &ce) && ce.Err != nil &&\n        strings.Contains(ce.Err.Error(), \"returned both an error and a WaitFunc\")\n}","tryCatchPattern":"// Contract violation; fix the command so it returns (WaitFunc, nil) XOR (nil, error).","preventionTips":["Return either a WaitFunc or an error from Run — never both.","On partial failure, capture the error inside the WaitFunc.","Test the two return values are never simultaneously non-nil."],"tags":["script","internal-error","command-contract","test-framework"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}