NationalSecurityAgency/ghidra · error · ValueError

No batch to end

Error message

No batch to end

What it means

Raised by Client.end_batch() when there is no current batch (cur_batch is None). Batching is reference-counted via start_batch/end_batch; end_batch may only be called when a batch is active. Mismatched calls corrupt the counter, so the library refuses rather than silently misbehave.

Source

Thrown at Ghidra/Debug/Debugger-rmi-trace/src/main/py/src/ghidratrace/client.py:965

        return f"<ghidratrace.Client {self.s}>"

    def close(self) -> None:
        self.s.close()
        self.receiver.shutdown()

    def start_batch(self) -> Batch:
        with self._block:
            if self.cur_batch is None:
                self.cur_batch = Batch()
            self.cur_batch.inc()
            return self.cur_batch

    def end_batch(self) -> Optional[List[Any]]:
        cb = None
        with self._block:
            cb = self.cur_batch
            if cb is None:
                raise ValueError("No batch to end")
            if 0 == cb.dec():
                self.cur_batch = None
                return cb.results()
        return None

    @contextmanager
    def batch(self) -> Generator[Batch, None, Optional[List[Any]]]:
        """Execute a number of RMI calls in an asynchronous batch.

        This returns a context manager, meant to be used as follows:

           with client.batch():
               trace.set_value(...)
               trace.set_value(...)
               ...

        This is highly recommended when you know you will be making many rapid
        RMI calls. All calls to the API that could involve RMI will instead

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Prefer the context manager: with client.batch(): ... which balances start/end automatically.
  2. If managing manually, ensure every start_batch() has exactly one matching end_batch(), including in except/finally.
  3. Track the batch handle returned by start_batch() and only end when you hold one.

Example fix

# before
cb = client.start_batch()
try:
    do_work()
finally:
    client.end_batch()  # fails if start_batch wasn't reached or already ended
# after
with client.batch():
    do_work()
Defensive patterns

Strategy: try-catch

Validate before calling

cb = client.start_batch()
assert cb is not None  # then end once

Try / catch

try:
    client.end_batch()
except ValueError:
    # batch already ended/not started; log and continue

Prevention

When it happens

Trigger: Calling end_batch() without a prior start_batch(); calling end_batch() more times than start_batch(); an exception path that double-ends a batch.

Common situations: Manual batch management instead of the provided context manager; error handling that calls end_batch in a finally without checking start succeeded; copy-paste across code paths.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/52e75925fa9eba96. Report an issue: GitHub.