From 970afdece94c955162226d09d9f2c587ffb0ddab Mon Sep 17 00:00:00 2001 From: pratapalakshmi <137189067+pratapalakshmi@users.noreply.github.com> Date: Tue, 2 Dec 2025 14:41:13 +0530 Subject: [PATCH] =?UTF-8?q?[PAI-894]=20fix:=20Enhance=20error=20handling?= =?UTF-8?q?=20and=20display=20in=20action=20status=20block=20and=20su?= =?UTF-8?q?=E2=80=A6=20(#4646)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pi/services/retrievers/pg_store/chat.py | 18 ++++++++++- .../pi-chat/actions/action-status-block.tsx | 30 ++++++++++++------- .../ee/components/pi-chat/actions/summary.tsx | 22 ++++++++++---- apps/web/ee/services/pi-chat.service.ts | 6 +++- apps/web/ee/store/pi-chat/pi-chat.ts | 25 ++++++++++++++-- apps/web/ee/types/pi-chat.d.ts | 18 ++++++++--- 6 files changed, 96 insertions(+), 23 deletions(-) diff --git a/apps/pi/pi/services/retrievers/pg_store/chat.py b/apps/pi/pi/services/retrievers/pg_store/chat.py index 4a3ebd30cd..270f67351f 100644 --- a/apps/pi/pi/services/retrievers/pg_store/chat.py +++ b/apps/pi/pi/services/retrievers/pg_store/chat.py @@ -325,7 +325,23 @@ async def extract_execution_status_from_flow_steps( if is_executed and is_successful: action_data["message"] = "Action completed successfully" elif is_executed and not is_successful: - action_data["error"] = "Action failed" + # Prefer the detailed execution_error from the flow step, then fall back to execution_result + error_message = None + try: + if getattr(step, "execution_error", None): + error_message = str(step.execution_error) + elif step.execution_data and isinstance(step.execution_data, dict): + exec_result = step.execution_data.get("execution_result") + if exec_result: + error_message = str(exec_result) + except Exception: + error_message = None + + # Truncate very long errors to keep the response compact + if error_message: + action_data["error"] = f"{error_message[:200]}..." if len(error_message) > 200 else error_message + else: + action_data["error"] = None actions.append(action_data) diff --git a/apps/web/ee/components/pi-chat/actions/action-status-block.tsx b/apps/web/ee/components/pi-chat/actions/action-status-block.tsx index 3116144bf3..a629ebaf92 100644 --- a/apps/web/ee/components/pi-chat/actions/action-status-block.tsx +++ b/apps/web/ee/components/pi-chat/actions/action-status-block.tsx @@ -21,8 +21,9 @@ type TProps = { const ActionStatusBlock = (props: TProps) => { // props + const { isLatest, isPiThinking, workspaceSlug, workspaceId, query_id, activeChatId, isPiTyping, dialogue } = props; - const { execution_status, action_summary, actions } = dialogue; + const { execution_status, action_summary, actions, action_error } = dialogue; // states const [isExecutingAction, setIsExecutingAction] = useState(false); // store @@ -43,7 +44,7 @@ const ActionStatusBlock = (props: TProps) => { setToast({ type: TOAST_TYPE.ERROR, title: "Action failed!", - message: e?.detail, + message: e?.error ?? e?.detail ?? e?.message ?? "Unable to execute action.", }); } finally { setIsExecutingAction(false); @@ -78,15 +79,24 @@ const ActionStatusBlock = (props: TProps) => { ); // Render summary if execution status is executing or action summary is present + const shouldShowSummary = execution_status === EExecutionStatus.EXECUTING || Boolean(action_summary); + + if (!shouldShowSummary && !action_error) return null; + return ( - (execution_status === EExecutionStatus.EXECUTING || action_summary) && ( - - ) +
+ {action_error && ( +
{action_error}
+ )} + {shouldShowSummary && ( + + )} +
); }; diff --git a/apps/web/ee/components/pi-chat/actions/summary.tsx b/apps/web/ee/components/pi-chat/actions/summary.tsx index 21cd189283..e309748fdf 100644 --- a/apps/web/ee/components/pi-chat/actions/summary.tsx +++ b/apps/web/ee/components/pi-chat/actions/summary.tsx @@ -109,11 +109,23 @@ export const SummaryBlock = observer((props: TProps) => {
{groupedArtifacts.failed.map((artifact) => ( -
-
{getIcon(artifact.artifact_type, "", "text")}
-
- {artifact.entity_name || "Unknown"} -
+
+ {(artifact.entity_name || artifact.action) && ( +
+
{getIcon(artifact.artifact_type, "", "text")}
+
+ {artifact.entity_name || artifact.action} +
+
+ )} + {artifact.error && ( +
+ {artifact.error} +
+ )} + {!artifact.error && artifact.message && ( +
{artifact.message}
+ )}
))}
diff --git a/apps/web/ee/services/pi-chat.service.ts b/apps/web/ee/services/pi-chat.service.ts index 79f4e6e75e..7518c1c692 100644 --- a/apps/web/ee/services/pi-chat.service.ts +++ b/apps/web/ee/services/pi-chat.service.ts @@ -282,7 +282,11 @@ export class PiChatService extends APIService { return this.post(`/api/v1/chat/execute-action/`, data) .then((response) => response?.data) .catch((error) => { - throw error?.response?.data; + throw ( + error?.response?.data ?? { + error: error?.message ?? "Unable to execute action", + } + ); }); } diff --git a/apps/web/ee/store/pi-chat/pi-chat.ts b/apps/web/ee/store/pi-chat/pi-chat.ts index 7e03e587e9..73c16f458b 100644 --- a/apps/web/ee/store/pi-chat/pi-chat.ts +++ b/apps/web/ee/store/pi-chat/pi-chat.ts @@ -256,10 +256,25 @@ export class PiChatStore implements IPiChatStore { dialogue.actions.forEach((action) => { const artifact = this.artifactsStore.getArtifact(action.artifact_id); if (!artifact) return; + + // Pull in error/message/entity info from the action as a backup. + // This way, users still see why an action failed even after a page refresh. + const mergedArtifact: TArtifact = { + ...artifact, + action: artifact.action || action.action, + artifact_type: artifact.artifact_type || action.artifact_type, + entity_name: artifact.entity_name || action.entity?.entity_name, + entity_url: artifact.entity_url || action.entity?.entity_url, + // If we have live artifact error/message, use that; otherwise, fall back + // to the error/message that came back with the chat history. + error: artifact.error ?? action.error, + message: artifact.message ?? action.message, + }; + if (action.success) { - response.successful.push(artifact); + response.successful.push(mergedArtifact); } else { - response.failed.push(artifact); + response.failed.push(mergedArtifact); } }); return response; @@ -913,6 +928,8 @@ export class PiChatStore implements IPiChatStore { issue_identifier: action.entity?.issue_identifier, is_executed: true, success: action.success, + error: action.error, + message: action.message, }); if (action.success) { actionableEntities.push(action.artifact_type); @@ -920,6 +937,7 @@ export class PiChatStore implements IPiChatStore { }); // update dialogue dialogue.execution_status = EExecutionStatus.COMPLETED; + dialogue.action_error = undefined; dialogue.action_summary = response.action_summary; dialogue.actions = response.actions; this.updateDialogue(chatId, actionId, dialogue); @@ -928,6 +946,9 @@ export class PiChatStore implements IPiChatStore { } catch (error) { console.error(error); dialogue.execution_status = EExecutionStatus.COMPLETED; + dialogue.action_error = + (error as any)?.error ?? (error as any)?.detail ?? (error as any)?.message ?? "Unable to execute action."; + dialogue.action_summary = undefined; this.updateDialogue(chatId, actionId, dialogue); throw error; } diff --git a/apps/web/ee/types/pi-chat.d.ts b/apps/web/ee/types/pi-chat.d.ts index 91495604bf..40f077f407 100644 --- a/apps/web/ee/types/pi-chat.d.ts +++ b/apps/web/ee/types/pi-chat.d.ts @@ -87,6 +87,8 @@ export type TArtifact = { is_editable: boolean; is_executed: boolean; success: boolean; + error?: string; + message?: string; artifact_type: string; entity_id?: string; entity_url?: string; @@ -120,6 +122,15 @@ export type TArtifact = { success?: boolean; }; +export type TArtifactWithEntity = TArtifact & { + entity?: { + entity_id: string; + entity_url: string; + issue_identifier: string; + entity_name: string; + }; +}; + export type TDialogue = { query_id?: string; answer_id?: string; @@ -130,7 +141,7 @@ export type TDialogue = { reasoning?: string; isPiThinking: boolean; execution_status?: EExecutionStatus; - actions?: TArtifact[]; + actions?: TArtifactWithEntity[]; action_summary?: { completed: number; duration_seconds: number; @@ -138,6 +149,7 @@ export type TDialogue = { total_planned: number; }; attachment_ids?: string[]; + action_error?: string; }; export type TChatHistory = { @@ -167,9 +179,7 @@ export type TAction = { export type TExecuteActionResponse = { status: string; message: string; - actions: Array< - TArtifact & { entity?: { entity_id: string; entity_url: string; issue_identifier: string; entity_name: string } } - >; + actions: TArtifactWithEntity[]; action_summary?: { completed: number; duration_seconds: number;