{"record":{"id":"d04a0c75695c7f14","repo":"vxcontrol/pentagi","slug":"command-execution-timeout-v-partial-output-s","errorCode":null,"errorMessage":"command execution timeout (%v). Partial output: %s. HINT: If this is an interactive command (shell/REPL/listener), use detach=true. For long batch commands, wrap with shell timeout utility: 'timeout %d <command>' to ensure clean completion","messagePattern":"command execution timeout \\((.+?)\\)\\. Partial output: (.+?)\\. HINT: If this is an interactive command \\(shell/REPL/listener\\), use detach=true\\. For long batch commands, wrap with shell timeout utility: 'timeout (.+?) <command>' to ensure clean completion","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/tools/terminal.go","lineNumber":298,"sourceCode":"\tgo func() {\n\t\t_, copyErr := io.Copy(&dst, resp.Reader)\n\t\terrChan <- copyErr\n\t}()\n\n\tselect {\n\tcase err := <-errChan:\n\t\tif err != nil && err != io.EOF {\n\t\t\treturn \"\", fmt.Errorf(\"failed to copy output: %w\", err)\n\t\t}\n\tcase <-ctx.Done():\n\t\t// Close the response to unblock io.Copy\n\t\tresp.Close()\n\n\t\t// Wait for the copy goroutine to finish\n\t\t<-errChan\n\n\t\tsuggestedTimeout := max(int(timeout.Seconds())-10, 10)\n\t\treturn \"\", fmt.Errorf(\n\t\t\t\"command execution timeout (%v). Partial output: %s. \"+\n\t\t\t\t\"HINT: If this is an interactive command (shell/REPL/listener), use detach=true. \"+\n\t\t\t\t\"For long batch commands, wrap with shell timeout utility: 'timeout %d <command>' to ensure clean completion\",\n\t\t\tctx.Err(),\n\t\t\ttruncateString(dst.String(), 500),\n\t\t\tsuggestedTimeout,\n\t\t)\n\t}\n\n\t// wait for the exec process to finish\n\t_, err = t.dockerClient.ContainerExecInspect(ctx, id)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to inspect exec process: %w\", err)\n\t}\n\n\tresults := dst.String()\n\t// Style system output with color coding\n\tstyledOutput := fmt.Sprintf(\"%s%s%s%s\", ansiColorSystemMsg, results, ansiColorReset, ansiLineTerminator)","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/tools/terminal.go#L280-L316","documentation":"Raised when the exec command does not finish within the configured timeout (the parent context deadline). The library returns any partial output collected so far plus an actionable HINT telling the caller to use detach=true for interactive commands or wrap long batch commands with the shell 'timeout' utility for clean completion.","triggerScenarios":"ExecCommand → getExecResult: ctx.Done() fires while waiting on errChan because the command ran longer than the timeout passed to ExecCommand (interactive shells, REPLs, listeners, or long scans).","commonSituations":"Running nmap/burp/long brute-force scans past the timeout; accidentally starting an interactive shell (bash, python REPL) or a network listener without detach=true; forgetting the container has no built-in command timeout.","solutions":["Re-run with detach=true for interactive or very long commands, then poll results","Prefix the command with the shell timeout utility, e.g. 'timeout 120 nmap ...'","Increase the timeout argument passed to ExecCommand if the command legitimately needs longer","Use the suggestedTimeout value embedded in the error message as a guide (timeout-10s, min 10s)"],"exampleFix":"// before\nout, err := term.ExecCommand(ctx, flowID, \"nmap -sV -p- target\", false)\n// after\nout, err := term.ExecCommand(ctx, flowID, \"timeout 300 nmap -sV -p- target\", false)\n// or for interactive: term.ExecCommand(ctx, flowID, \"bash -i\", true)","handlingStrategy":"validation","validationCode":"// pick a timeout comfortably above the command's expected runtime\nconst cmdTimeout = 5 * time.Minute\n// wrap long batch commands instead of relying on the exec timeout:\nwrapped := fmt.Sprintf(\"timeout %d %s\", int(cmdTimeout.Seconds()), cmd)\n_, err := term.ExecCommand(ctx, flowID, wrapped, false)","typeGuard":null,"tryCatchPattern":"out, err := term.ExecCommand(ctx, flowID, cmd, false)\nif err != nil && strings.Contains(err.Error(), \"command execution timeout\") {\n    // partial output is embedded in the message; decide: detach re-run or longer timeout\n    return handleTimeout(err)\n}","preventionTips":["Never start interactive shells/REPLs/listeners without detach=true","Prefix long scans with the shell 'timeout' utility","Size the timeout from the command's worst-case runtime, not its average","Parse the suggestedTimeout hint in the message when tuning retries"],"tags":["docker","timeout","exec","interactive-command"],"backgroundTag":"command-execution-timeout","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}