google / google/ax

`ax exec --resume` on pending execution fails with 'no input messages queued'

Open
#190 0 comments 0 reactions 1 assignee Claimed by @joycel-github View on GitHub
cmd/exec controller
Dominant language
Go
Stars
2k
Forks
120
PR merge metrics
No merged PRs in 30d

Description

## Summary

`ax exec --resume` against a conversation that's in `STATE_PENDING`
(e.g., the harness died mid-stream on a previous turn) fails
with:

```
harness execution failed: no input messages queued for execution turn
```

The gRPC `HarnessService.Connect` stream is not opened.

## Reproduction (verified end-to-end against a local Antigravity
harness server talking to Vertex / Gemini 2.5 Flash)

Force a PENDING execution by killing the harness mid-stream:

```
$ ax exec --conversation t5 --input "Count from 1 to 30 slowly, ..." &
(after ~4s, while streaming)
$ systemctl --user kill -s KILL axharness
→ ax exits with: gRPC harness streaming failure: rpc error: code = Unavailable
→ event log: 2 events, both STATE_PENDING
```

Bring the harness back and try to resume:

```
$ systemctl --user start axharness
$ ax exec --conversation t5 --resume
→ harness execution failed: no input messages queued for execution turn
```

## Root cause

[`Controller.Exec`](https://github.com/google/ax/blob/cdc7688/internal/controller/controller.go#L90-L103)
that calls `h.Start(...)` followed directly by `exec.Run(...)` with
**no intervening `exec.Queue(...)`**:

```go
if state == proto.State_STATE_PENDING {
exec, err := h.Start(ctx, req.ConversationId)
if err != nil { ... }
defer exec.Close(ctx)

if err := exec.Run(ctx, hhandler); err != nil { // 👈 no Queue
return fmt.Errorf("harness execution failed: %w", err)
}
}
```

But [`AntigravityHarness.Run`](https://github.com/google/ax/blob/cdc7688/internal/harness/antigravity.go#L99-L101)
rejects an empty queue:

```go
if len(inputs) == 0 {
return fmt.Errorf("no input messages queued for execution turn")
}
```

That's the source of the error string above — it comes from the Go
client (`internal/harness/antigravity.go:100`), not from the python
harness server. The request never leaves the Go process.

Note: [`SubstrateHarness.Run`](https://github.com/google/ax/blob/cdc7688/internal/harness/substrate.go)
does not have this guard, which is a separate inconsistency (cf. #181
side observation).

## Why removing the guard isn't sufficient

If the guard is removed, the request reaches the python harness
server, which then hits its own equivalent rejection in
[`harness_server.py:172-178`](https://github.com/google/ax/blob/cdc7688/python/antigravity/harness_server.py#L172-L178):

```python
if not ax_messages:
yield ax_pb2.HarnessResponse(
end=ax_pb2.HarnessEnd(state=STATE_FAILED,
error_message="No messages found in start payload"))
return
```

So the "empty `HarnessStart.Messages` means resume" heuristic
currently conflicts at two layers (Go client + python server). The
wire protocol has no explicit way to express "resume intent vs new
turn with no input".

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.