@mcp.tool()
async def delete_document(
doctype: str,
name: str
) -> str:
"""
Delete a document from Frappe.
This tool handles document deletion with comprehensive error handling,
providing detailed feedback about why deletions fail and actionable
guidance for resolving common deletion blockers.
Args:
doctype: DocType name
name: Document name (case-sensitive)
Returns:
Success message if deleted, or detailed error information with
corrective actions for linked documents, permissions, or constraints.
"""
try:
client = get_client()
# First, get the current document to check its status and understand constraints
try:
doc_response = await client.get(f"api/resource/{doctype}/{name}")
doc_data = doc_response.get("data", {})
current_docstatus = doc_data.get("docstatus", None)
if current_docstatus is None:
return f"Error: Could not retrieve document {doctype} '{name}'. Document may not exist."
# Provide guidance based on document status
if current_docstatus == 1:
return (
f"⚠️ Document {doctype} '{name}' is submitted (docstatus=1). "
f"Submitted documents cannot be deleted. You must cancel it first using cancel_document, "
f"then delete the cancelled document."
)
elif current_docstatus == 2:
# Cancelled documents can usually be deleted, but may have constraints
pass # Continue with deletion attempt
elif current_docstatus == 0:
# Draft documents should be deletable, but may have linked documents
pass # Continue with deletion attempt
except Exception as get_error:
# Document might not exist, which is fine for deletion
pass
# Attempt to delete the document
response = await client.delete(f"api/resource/{doctype}/{name}")
if response.get("message") == "ok":
return f"✅ Document {doctype} '{name}' successfully deleted."
else:
return f"⚠️ Deletion may have succeeded but response format unexpected: {json.dumps(response, indent=2)}"
except FrappeApiError as api_error:
# Handle specific Frappe API errors with detailed information
if api_error.response_data:
error_data = api_error.response_data
# Check for validation errors in the response
if "exception" in error_data:
exception_msg = error_data["exception"]
# Extract user-friendly error messages
if "ValidationError" in str(exception_msg):
# Common deletion validation errors
if "linked" in str(exception_msg).lower() or "referenced" in str(exception_msg).lower():
# Extract linked document information directly from the error message
linked_docs = _extract_linked_docs_from_error(str(exception_msg))
if linked_docs:
linked_info = "\n".join([f" - {doc['doctype']} '{doc['name']}'" for doc in linked_docs])
return (
f"❌ Deletion failed: Document {doctype} '{name}' cannot be deleted because it is linked to other documents.\n"
f"Blocking document(s):\n{linked_info}\n"
f"To resolve: Delete, cancel, or unlink these documents first, then retry deletion."
)
else:
return (
f"❌ Deletion failed: Document {doctype} '{name}' cannot be deleted because it is linked to other documents. "
f"Error details: {exception_msg}. "
f"To resolve: Find and delete/cancel the documents that reference this one, or remove the links first."
)
elif "Cannot delete" in str(exception_msg):
return (
f"❌ Deletion failed: {exception_msg}. "
f"This may be due to business rules, data integrity constraints, or linked transactions. "
f"Check for related documents that need to be handled first."
)
elif "submitted" in str(exception_msg).lower():
return (
f"❌ Deletion failed: Cannot delete submitted documents. "
f"Use cancel_document first to cancel {doctype} '{name}', then try deleting the cancelled document."
)
else:
# Generic validation error
return f"❌ Validation error: {exception_msg}. Please resolve the validation issues before deleting."
elif "PermissionError" in str(exception_msg):
return (
f"❌ Permission denied: You don't have sufficient permissions to delete {doctype} documents. "
f"Contact your system administrator to request Delete permission for {doctype}."
)
elif "IntegrityError" in str(exception_msg) or "foreign key" in str(exception_msg).lower():
return (
f"❌ Database constraint violation: Document {doctype} '{name}' cannot be deleted due to foreign key constraints. "
f"Error: {exception_msg}. "
f"This usually means other records reference this document. Find and handle those references first."
)
else:
# Other exceptions with helpful context
return f"❌ Deletion failed: {exception_msg}"
# Check for server messages with more details
if "_server_messages" in error_data:
try:
messages = json.loads(error_data["_server_messages"])
if messages:
msg_data = json.loads(messages[0])
user_message = msg_data.get("message", "Unknown error")
# Parse common server messages for actionable guidance
if "linked" in user_message.lower():
return (
f"❌ Deletion failed: {user_message}. "
f"To resolve: Identify and handle the linked documents first, then retry deletion."
)
else:
return f"❌ Deletion failed: {user_message}"
except (json.JSONDecodeError, KeyError, IndexError):
pass
# Handle HTTP status codes for additional context
if api_error.status_code == 403:
return (
f"❌ Access forbidden: You don't have permission to delete {doctype} '{name}'. "
f"Contact your administrator for Delete permissions on {doctype}."
)
elif api_error.status_code == 404:
return f"✅ Document {doctype} '{name}' not found - it may already be deleted or never existed."
elif api_error.status_code == 417:
# HTTP 417 errors may contain linked document information in the response
error_text = str(api_error)
if api_error.response_data:
error_text += " " + str(api_error.response_data)
linked_docs = _extract_linked_docs_from_error(error_text)
if linked_docs:
linked_info = "\n".join([f" - {doc['doctype']} '{doc['name']}'" for doc in linked_docs])
return (
f"❌ Deletion failed with HTTP 417 error. Found blocking document(s):\n{linked_info}\n"
f"These documents may be preventing deletion. Try deleting, cancelling, or unlinking them first."
)
else:
return (
f"❌ Deletion failed with HTTP 417 error. This may be due to server configuration issues "
f"or complex validation constraints. Error: {api_error}. "
f"Check for business rule violations, permissions, or linked documents."
)
else:
return f"❌ Deletion failed: {api_error}"
except Exception as error:
return _format_error_response(error, "delete_document")