[Bug]: "this action has not been executed" message is duplicated
Is there an existing issue for the same bug?
- [X] I have checked the existing issues.
Describe the bug and reproduction steps
As in the attached screenshot, the message for "this action has not been executed" is duplicated multiple times.
We should only have the one that has the red line to the left of it.
It would be good to write unit tests that demonstrate the problem, confirm that they fail, and then fix the issue.
OpenHands Installation
app.all-hands.dev
OpenHands Version
No response
Operating System
None
Logs, Errors, Screenshots, and Additional Context
No response
An attempt was made to automatically fix this issue, but it was unsuccessful. A branch named 'openhands-fix-issue-6098' has been created with the attempted changes. You can view the branch here. Manual intervention may be required.
Additional details about the failure: Based on the provided information, the issue has not been successfully resolved. The AI agent has only mentioned reading a translation file but has not:
- Created or modified any code to fix the duplicate messages issue
- Written unit tests to demonstrate and verify the problem
- Implemented a solution to ensure only one message (with the red line) appears
- Provided any evidence that the duplicate message problem has been fixed
The last message only indicates that a file was read, which is just the beginning of understanding the issue, not a resolution. To consider this resolved, we would need to see actual code changes, test implementation, and confirmation that the duplicate messages no longer appear.
@neubig May I contribute to this issue?
@Srilekha2805 Of course! You don't need to ask 😃 You are most welcome, thank you!
Yes! And actually, thank you for asking, it makes it possible for me to assign you in GitHub
@neubig Any existing LLM api keys that I could use for running this locally?
For now, maybe you can use the Gemini API with gemini-2.0-flash, as it's free: https://ai.google.dev/pricing#1_5flash For consistent contributors we're happy to provide an API key to use Claude!
@neubig and @enyst I don't see this issue happening now, Could you guys confirm once from your end?
Maybe? I tested quickly with UI locally and on the hosted site, and I didn't see it. But this issue was intermittent, maybe it takes more testing to see it. I don't use the web UI very often, so I might not see intermittent things myself.
Ah, I just saw it. What I did was to press the Stop button in the chat, during an longer action execution... Then the agent was in STOPPED state:
This issue is stale because it has been open for 30 days with no activity. Remove stale label or comment or this will be closed in 7 days.
@neubig Could you please assign this issue to me?
@Riddhikshah21 I'm really sorry for the delay in responding. Just so you know though, nobody "needs" an assignment to take a look at an issue or propose PRs! We are an open source project, and hey, by definition, nobody needs anyone's permission to make it work the way they wish it worked.
Of course, it's sometimes useful to avoid duplicating work, although other times it's also useful to try different approaches to the same issue.
Personally, I started using assignments for myself relatively recently, and it was really because I failed to keep up with the things I needed to look at. So like a bookmark. 😅
If you still want it, just let me know.
Summarizing below the root cause and the reproduction steps.
I can create a PR to fix this issue, but before that, I'm curious if @openhands-agent can resolve this issue with this additional information 👀
Bug Analysis
The root cause seems that the observation message is processed twice in observations.ts. In detail:
- The error observation is sent by the Python backend server at
- https://github.com/All-Hands-AI/OpenHands/blob/fddbfce51aab385c4a5deaa7a7a41e26a68a32a3/openhands/controller/agent_controller.py#L525-L528
- The TypeScript frontend processes the message twice inÂ
observation.ts, specifically in the following two blocks:
- https://github.com/All-Hands-AI/OpenHands/blob/fddbfce51aab385c4a5deaa7a7a41e26a68a32a3/frontend/src/services/observations.ts#L56-L58
- https://github.com/All-Hands-AI/OpenHands/blob/fddbfce51aab385c4a5deaa7a7a41e26a68a32a3/frontend/src/services/observations.ts#L238-L249
Repro Test Case (Broken)
The following script reproduces the current behavior of the TypeScript frontend. Note that this test case demonstrates the bug and should NOT pass in the fixed version.
import { handleObservationMessage } from "#/services/observations";
import store from "#/store";
import { ObservationMessage } from "#/types/message";
import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("#/store", () => ({
default: {
dispatch: vi.fn(),
},
}));
describe("handleObservationMessage", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("currently duplicates error messages", () => {
// Note: steps to extract the actual ObservationMessage object is described in the `Additional Context` section below.
const errorMessage: ObservationMessage = {
id: 14,
timestamp: "2025-04-14T13:37:54.451843",
source: "agent",
message: "The action has not been executed.",
cause: 12,
observation: "error",
content: "The action has not been executed.",
extras: {
error_id: "",
},
tool_call_metadata: {
function_name: "execute_bash",
tool_call_id: "test_id",
total_calls_in_response: 1
}
};
handleObservationMessage(errorMessage);
// Note: current behavior is as follows.
// - `The action has not been executed.` appears twice.
// - The first one is shown as an assistant message.
// - The second one is done as an assistant observation.
expect(store.dispatch).toHaveBeenCalledTimes(2);
expect(store.dispatch).toHaveBeenNthCalledWith(1, {
type: "chat/addAssistantMessage",
payload: "The action has not been executed.",
});
expect(store.dispatch).toHaveBeenNthCalledWith(2, {
type: "chat/addAssistantObservation",
payload: expect.objectContaining({
observation: "error",
content: "The action has not been executed.",
source: "user",
extras: {
error_id: "",
}
}),
});
});
});
Suggestion for Fix
Define ObservationType.ERROR and handle it only in either the switch (message.observation) or switch (observation) block. This design choice requires further discussion, as the role of the if (!message.extras?.hidden) block needs to be considered.
Additional Context: How to Inspect the Actual Observation Message Structure
To reproduce the exact structure of the observation messages being processed by the frontend, you can:
Add debug logging to observation.ts. (c.f., https://github.com/All-Hands-AI/OpenHands/commit/cf0542528c1992ebd32f507e0aa9b1e6b976c4c0)
console.log("=== Observation Message ===");
console.log(JSON.stringify(message, null, 2));
Run the servers:
make build
make setup-config
make run
Open localhost:3000 in the browser. Then,
- Start a conversation with the agent
- Prompt a long-running command (e.g., "Run the sleep command for 30 sec.")
- Click the stop button
- Open the browser's developer tools console
- You will see the raw JSON structure of the observation message that triggered the duplicate display
I've uploaded the complete observation message structure to this gist for reference.
@g-votte Thank you for looking into this!
openhands-agent can take a look at it, you may want to install it on your repo first, though: this way
@enyst cc: @neubig
Thanks for the guidance! The openhands-agent worked well and suggested a fix :robot:
I've refined the implementation into PR #7858. Would appreciate your review when you have a chance!
See also: agent's commit 2239ba0