Skip to content

Standardize file-tool error flow onto ToolErrorException so forward_tool_error_message works for internal file tools #455

Description

@andrii-novikov

QuickApps version

latest

Area

Toolsets (REST / DIAL deployment / MCP / internal)

What is the current state?

  • File / module: src/quickapp/dial_files_tooling/ (internal file tools), src/quickapp/common/tool_fallback/utils.py
  • Current behavior or pattern:
    • PR feat: expose MCP and HTTP Tool Errors to the LLM #408 introduced forward_tool_error_message on fallback strategies, which forwards the real error text to the LLM instead of only the generic fallback instructions. The gate lives in compose_tool_error_fallback_message (src/quickapp/common/tool_fallback/utils.py:10): the real message is forwarded only when the escaping exception is a ToolErrorException subclass.
    • ToolErrorException subclasses exist only for MCP tools (MCPToolErrorException) and REST tools (RestApiToolErrorException). The internal file tools (src/quickapp/dial_files_tooling/) never raise one.
    • Instead, the file tools let raw DialExceptions escape their tool bodies: _check_permission_denied handles only 403, and every except DialException site ends in a bare raise (e.g. _base_file_tool.py:128-130 in _list_folder_entries, _copy_file_tool.py:42-44, and the same pattern in the write/edit/delete/move tools).
    • Additionally, _DialFileTool._download_text (_base_file_tool.py:65-67) wraps any non-404 DialException — including Core 5xx server errors — into InvalidToolCallParameterException("path", ...).
  • How it got this way (if known): the file tools predate feat: expose MCP and HTTP Tool Errors to the LLM #408's ToolErrorException forwarding contract; their error handling was written around the parameter-retry path only.

Why is this debt?

Proposed remediation

  • Approach: make every failure escaping a file tool one of exactly two standardized shapes — no raw DialException leaks:
    1. InvalidToolCallParameterException for parameter-shaped failures the model can fix and retry (not found 404, access denied 403, etag conflict, bad range/pattern) — handled today by the dedicated branch in StagedBaseTool.__run_tool_body (src/quickapp/common/staged_base_tool.py:129).
    2. A new FileToolErrorException(ToolErrorException) for genuine tool/server failures (e.g. Core 5xx), mirroring src/quickapp/mcp_tooling/_mcp_tool_error_exception.py. Being a ToolErrorException, it makes compose_tool_error_fallback_message forward the real text when forward_tool_error_message=True — zero changes to the fallback machinery.
  • Affected modules: src/quickapp/dial_files_tooling/_base_file_tool.py (a translation helper replacing _check_permission_denied, applied at every except DialException site), the write/edit/delete/copy/move tools, and _tool_configs.py (ship forward_tool_error_message=True defaults for all nine file tools).
  • Migration / rollout considerations: none — the file-tool configs are hardcoded (not overridable from app manifests), and the change only improves the message the LLM sees.

A design doc under docs/designs/ will follow in a separate PR.

Alternatives considered

  • Loosening compose_tool_error_fallback_message to forward the text of any exception: rejected — the ToolErrorException gate is deliberate (feat: expose MCP and HTTP Tool Errors to the LLM #408); arbitrary exception reprs may leak internals and noisy stack context.
  • Wrapping every DialException into InvalidToolCallParameterException (extending the current _download_text pattern): rejected — server errors are not parameter errors; "fix the parameter and retry" is misleading and bypasses configured fallback strategies.

Additional information

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions