Skip to content

Long-running bash commands with large outputs cause truncation and agent retry loops #11313

Closed as not planned

Description

@nabilfreeman

Description

Long-running bash commands with verbose output are being truncated and/or timed out, causing agents to re-attempt commands instead of polling the saved tool output. This creates serious idempotency issues in workflows where commands should run to completion.

Current behavior:

  1. Bash commands have a hardcoded 2-minute default timeout (src/tool/bash.ts:21)
  2. Output is truncated at 2,000 lines or 50KB (src/tool/truncation.ts:10-11)
  3. When output is truncated, full output is saved to ~/.opencode/data/tool-output/, but agents frequently re-run the command instead of reading the saved output
  4. Commands are killed after timeout, even if they would have completed successfully

The problem:

  • Commands that produce large build logs, test outputs, or verbose diagnostics get truncated
  • Agents see truncated output and often retry the command instead of using Read with offset/limit on the saved output file
  • This creates duplicate work, wasted API calls, and potential side effects from non-idempotent commands
  • For CI/CD workflows, this behavior is unacceptable - commands should never exit until a proper exit code is received

Current configuration options:

  • OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS - env var for timeout (exists)
  • No configuration for MAX_LINES or MAX_BYTES truncation limits (hardcoded)

Proposed solution:
Add opencode.json configuration for these limits:

{
  "experimental": {
    "bash": {
      "timeout_ms": 600000,      // 10 minutes
      "max_lines": 10000,        // or null to disable
      "max_bytes": 512000        // or null to disable
    }
  }
}

This would allow users to configure behavior appropriate for their workflows without needing environment variables.

Plugins

None

OpenCode version

Current (main branch)

Steps to reproduce

  1. Run a command that produces more than 2,000 lines of output (e.g., npm install with verbose logging, large test suite)
  2. Observe output is truncated with message about saved file
  3. Watch agent retry the command instead of reading the saved output
  4. For timeout: run any command that takes longer than 2 minutes

Screenshot and/or share link

No response

Operating System

macOS

Terminal

No response

Activity

  1. added
    perfIndicates a performance issue or need for optimization
    on Jan 30, 2026
  2. github-actions commented on Jan 30, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Feel free to ignore if your specific case addresses concerns not covered by these issues.

  3. nabilfreeman commented on Jan 30, 2026

    @nabilfreeman
    ContributorAuthor

    Context from Related Issues

    After reviewing the related issues, here's what I've gathered:

    Timeout configuration - This has been addressed. OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS was added in December 2025 (#1721, #3950, #4197). The max timeout cap was removed, so agents can set arbitrarily long timeouts per-command. The env var sets the default.

    Output truncation - This is the unaddressed gap. The hardcoded limits in truncation.ts (2000 lines, 50KB) have no configuration mechanism. Related issues:

    The maintainer perspective (from @rekram1-node in #3950):

    "we will have something more appropriate in the config later, for now we will have: OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT (supported in next release also this is in milliseconds)"

    This suggests the pattern for new settings: start with OPENCODE_EXPERIMENTAL_* env vars, then graduate to opencode.json config once the feature is stable.


    Proposed First Phase (Safe, Low-Risk)

    Following the established pattern, add two new env vars to flag.ts:

    export const OPENCODE_EXPERIMENTAL_MAX_LINES = number("OPENCODE_EXPERIMENTAL_MAX_LINES")
    export const OPENCODE_EXPERIMENTAL_MAX_BYTES = number("OPENCODE_EXPERIMENTAL_MAX_BYTES")

    Then in truncation.ts:

    export const MAX_LINES = Flag.OPENCODE_EXPERIMENTAL_MAX_LINES ?? 2000
    export const MAX_BYTES = Flag.OPENCODE_EXPERIMENTAL_MAX_BYTES ?? (50 * 1024)

    This is ~4 lines of code, follows the exact pattern used for OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS, and gives power users an escape hatch while the team decides on the proper config schema.


    Why This Issue Should Be Primary

    This issue consolidates the truncation + timeout + idempotency concerns that are scattered across multiple threads:

    The unique contribution here is identifying the agent retry loop problem: when output is truncated, agents often re-run commands instead of reading the saved output file. This creates real workflow issues beyond just "I want more output" - it causes duplicate side effects and wasted compute.

    Happy to submit a PR for the env var approach if maintainers confirm this is the right direction.

  4. pserey commented on Mar 10, 2026

    @pserey

    Hello! I tested the solution proposed in the last comment and it worked perfectly on my end.

    In my case, I’m developing some MCP tools that return internal documentation, and the current ~50KB tool response limit often truncates the docs. When that happens, the agent tends to misinterpret the truncated response and enters a retry/loop pattern.

    After building a version with the solution proposed by @nabilfreeman, the agent behaved as expected: long tool outputs were handled without getting stuck in a retry loop.

    Just a +1 to the proposed direction of making these limits configurable (timeout + max_bytes/max_lines) via env vars -> opencode.json so it’s not hardcoded and can be tuned per workflow.

  5. nabilfreeman commented on Mar 11, 2026

    @nabilfreeman
    ContributorAuthor

    @pserey really glad to hear it!

    Command output truncation has been the bane of my existence recently.

    It's really frustrating that so many of the model providers have trained/steered their models to aggressively truncate context. I firmly believe this makes the models perform worse and is entirely done for cost saving measures.

    Opencode's truncation is not for the same reason but it's holding back large context workflows.

    If we can get this behaviour changed, then people can finally use npx fullcontext npm test etc. to bypass the head and tail abuse by Claude et al.

  6. QC2168 commented on Mar 12, 2026

    @QC2168

    I configured OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS in bashrc, but it did not take effect

  7. nabilfreeman commented on Mar 23, 2026

    @nabilfreeman
    ContributorAuthor

    @thdxr hey there! I saw you are assigned to this issue. Just wondering if you have any thoughts? I am happy to plan out and do the PR for this. We are desperate for this addition to OC and are willing to invest the labor to make it happen.

    Because it will probably involve adding settings to the opencode.json file, it would be very helpful to have some steering from the Opencode team on the best approach here.

    Many thanks

  8. assigned and unassigned on Apr 26, 2026
  9. rekram1-node commented on Apr 27, 2026

    @rekram1-node
    Collaborator

    You can control truncation logic by using these fields in your opencode.json (pay no attention to the numbers):

     "tool_output": {
        "max_bytes": 10,
        "max_lines": 10,
      },
    

    You can control the default timeout using: OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS (defaults to 2 minutes but the agent can also change the timeout whenever it calls the tool so it can override it too).

    We should prolly add a simple way to disable timeout too

  10. timwkosatec commented on Apr 27, 2026

    @timwkosatec

    seems like there was a duplicate issue that just fixed what we asked here except for the timeout

    Issue: #22565
    PR: #23770

  11. nabilfreeman commented on Apr 27, 2026

    @nabilfreeman
    ContributorAuthor

    Thank you guys for the improvement! 👏

    @rekram1-node Disabling timeout would be greatly appreciated. We are still relying on tons of prompt steering and tool layers to use nohup all the time which is convoluted.

  12. removed
    perfIndicates a performance issue or need for optimization
    on May 3, 2026
  13. nabilfreeman commented on May 11, 2026

    @nabilfreeman
    ContributorAuthor

    Quick update here, we now have tool output configured in our harness plus reliable injection of the experiment environment flag.

    We have been able to completely eliminate timeouts and truncations as a result!

    Start opencode with this command

    OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS=7200000 opencode
    

    Bash commands will run for 2 hours before timing out (BUT, you must apply the below plugin until the related issue is fixed)

    opencode.json

    ...
    "tool_output": {
        "max_lines": 50000,
        "max_bytes": 1000000
      },
    ...
    

    50k lines or 1mb of text on any bash output

    Was still getting timeouts despite the above!

    One small problem we had to work around. Agents don't know about the overridden timeout, they still think it's 2 minutes. So, they often try to put a 10 minute timeout on the bash instead of our 2 hour timeout. I have outlined this bug here: #25913

    The solution was to create a plugin that intercepts the agent's bad timeout usage:

    bash-timeout-clamp.plugin.ts

    /**
     * Clamp the opencode bash tool's `timeout` parameter to a 2-hour minimum.
     *
     * Opencode's bash tool computes `params.timeout ?? DEFAULT_TIMEOUT` (see
     * opencode source: packages/opencode/src/tool/bash.ts ~line 597). Without
     * this plugin, an LLM that supplies any `timeout` — even a too-small one
     * like 60000 (60s) — overrides the operator's default and causes premature
     * SIGTERM on long-running commands.
     *
     * This plugin uses the `tool.execute.before` hook to mutate `args.timeout`
     * by reference, raising any value below MIN_TIMEOUT_MS to MIN_TIMEOUT_MS.
     * Values >= MIN_TIMEOUT_MS, undefined values, and non-finite values are
     * left untouched.
     *
     * @type {import("@opencode-ai/plugin").Plugin}
     */
    
    const MIN_TIMEOUT_MS = 7_200_000; // 2 hours; mirrors DEFAULT_BASH_TIMEOUT_MS in harness
    
    export default async () => ({
      "tool.execute.before": async (
        input: { tool: string; sessionID: string; callID: string },
        output: { args: Record<string, unknown> },
      ) => {
        if (input.tool !== "bash") return;
    
        const t = output.args.timeout;
        // Only clamp finite numbers below the floor; leave undefined / non-numbers
        // for opencode's schema validator to handle.
        if (typeof t === "number" && Number.isFinite(t) && t < MIN_TIMEOUT_MS) {
          output.args.timeout = MIN_TIMEOUT_MS;
        }
      },
    });

    Personal thoughts

    Respectfully I still think that Opencode is way too opinionated on this, presumably for context management / cost considerations. Restricting models' access to context reliably degrades quality as the trade-off.

  14. nabilfreeman commented on May 23, 2026

    @nabilfreeman
    ContributorAuthor

    There is still an open issue that needs fixing: #25913 - and the pull request #25917

    cc @rekram1-node

  15. rekram1-node commented on May 23, 2026

    @rekram1-node
    Collaborator

    @nabilfreeman what is it too opinionated about? Every agent has this same truncation logic ours is actually the least restrictive rn

  16. Keesan12 commented on May 23, 2026

    @Keesan12

    The remaining sharp edge here looks less like "how large should the defaults be?" and more like "does the model see the effective execution contract it is about to override?"

    If the operator sets a two-hour shell default plus larger tool_output limits, the agent still needs a machine-visible receipt before it chooses an explicit shorter timeout: effective timeout, effective max lines/bytes, persisted output path, and a recommended next action such as read_saved_output versus rerun_command. Without that, the model can accidentally turn an operator-level policy into a smaller per-call timeout and recreate the retry loop.

    In MartinLoop terms, I would treat truncated output or timeout as an admission decision for the next attempt: rerun only if the command or failure class changed; otherwise read the saved artifact. That might be a small fixture worth adding around this issue: "large output saved, assistant's next step reads the saved path instead of rerunning."

  17. nabilfreeman commented on May 23, 2026

    @nabilfreeman
    ContributorAuthor

    @nabilfreeman what is it too opinionated about? Every agent has this same truncation logic ours is actually the least restrictive rn

    @rekram1-node hey 👋

    Well, my comment is specifically about the pull request. That is fixing the problem that the tool description is hard coding 2 minutes no matter what you set for the experimental bash timeout arg. I don’t think there is much to argue about this, it’s just closing a gap in the implementation. If you guys could please take a look at that and merge, opencode would be improved.

    As for the opinionation - if I want to turn off truncation and timeouts as a harness engineer, why is the harness fighting me? If I have a 2 million context model and a 12+ hour agentic loop, I want to be able to use open code for the harness. There is no need for truncation or timeouts, they are not serving any useful purpose. You are right, you guys are the best platform right now. That’s why I’m using opencode and not something else. And actually you are so close to an unopinionated harness platform. I would prefer to use something that I believe will become industry standard instead of building something unmaintainable on Pi.

    The future of agentic development will be fine tuned custom harnesses and we need a deterministic platform to build on. Hoping that is opencode. You guys can achieve that while also serving people that want a turnkey solution for the short term. And I and other contributors am very willing to help and contribute towards that balance!

  18. rekram1-node commented on May 24, 2026

    @rekram1-node
    Collaborator

    @nabilfreeman totally valid, just wanted to be clear what ur need is, i actually have a separate PR that's open rn for allowing disabling of truncation. U can technically disable it by mutating tool output metadata in a plugin but its not documented and its non obvious

    These are good points! And yeah we are making progress, in next week or two we will launch a revamped opencode (opencode v2) that prioritizes composability and customization, killing poor old apis and having a much more intuitive surface along with some new features too.

  19. bartlettroscoe commented on Jun 19, 2026

    @bartlettroscoe
    Contributor

    @rekram1-node and @nabilfreeman, is this PR still needed or has it already been addressed with some other Issue & PR?

  20. github-actions commented on Aug 19, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions