Copying pages with Comala Document Management fails with HTTP 500 error
Summary
When attempting to copy a Confluence page that has a Comala Document Management (CDM) workflow, the operation fails with a 500 Internal Server Error.
This is typically caused by duplicate JSON content properties on the page (for example, comalaworkflowstasks or comalaworkflowparameters). This article explains how to identify affected pages using SQL and clean up duplicates using a Python script.
Symptoms
Users receive an HTTP 500 error immediately after clicking "Copy" on a page.
The issue only occurs on pages where Comala Document Management is used.
The following error appears in the
atlassian-confluence.log:
ERROR [http-nio-8090-exec-22] [apache.struts2.dispatcher.DefaultDispatcherErrorHandler] sendErrorResponse Exception occurred during processing request: Cannot create new content property: JsonContentProperty{id='null', key='comalaworkflowstasks', ...}
com.atlassian.confluence.api.service.exceptions.ConflictException: Cannot create new content property: ... [SimpleMessage{key='jsonproperty.duplicate.key', args=[], translation='null'}]Cause
This issue occurs when a page has duplicate entries for specific CDM content properties (for example, comalaworkflowstasks). Confluence's copy mechanism attempts to recreate these properties on the new page but fails when it encounters duplicates, resulting in a ConflictException.
Resolution
Step 1: Back up your database (recommended)
Creating a backup lets you quickly restore Confluence if anything goes wrong while identifying or deleting duplicate content properties. Direct database queries and property deletions can be destructive if misapplied, so a recent backup minimizes downtime and data loss.
Step 2: Identify affected pages
Run the following SQL query against your Confluence database to identify pages with duplicate content properties.
For PostgreSQL / MySQL / SQL Server
SELECT *
FROM content AS c
JOIN bodycontent AS b ON c.contentid = b.contentid
WHERE c.contenttype = 'CUSTOM'
AND c.title = 'comalaworkflowstasks'
AND c.pageid IN (
SELECT c.pageid
FROM content AS c
JOIN bodycontent AS b ON c.contentid = b.contentid
WHERE c.contenttype = 'CUSTOM'
AND c.title = 'comalaworkflowstasks'
GROUP BY c.pageid
HAVING COUNT(*) > 1
);For Oracle Database
SELECT *
FROM content c
JOIN bodycontent b
ON c.contentid = b.contentid
WHERE c.contenttype = 'CUSTOM'
AND c.title = 'comalaworkflowstasks'
AND c.pageid IN (
SELECT c2.pageid
FROM content c2
JOIN bodycontent b2
ON c2.contentid = b2.contentid
WHERE c2.contenttype = 'CUSTOM'
AND c2.title = 'comalaworkflowstasks'
GROUP BY c2.pageid
HAVING COUNT(*) > 1
);If your logs reference comalaworkflowparameters instead of comalaworkflowstasks, replace the title in the queries above accordingly.
Step 3: Delete duplicate properties
Use the following Python script to clean up the identified duplicate properties using the Confluence REST API.
How the script works:
Initial Connection: It first connects to your Confluence site using your username and password to ensure it is reachable (useful for environments where Atlassian tokens are not preferred).
Targeting: It maintains a list of Page IDs and property keys (for example,
comalaworkflowstasks,readack) to remove.Validation: For each page, the script checks if a property exists before attempting deletion.
If found, it attempts to delete it.
If missing or restricted, it logs the specific status and continues.
Logging: Every action (success, bad credentials, permissions, or server errors) is recorded in a text file with a timestamp.
Final Output: Once finished, the script provides the log file location for your review.
Prerequisites:
A Personal Access Token (PAT) from Confluence.
The user associated with the PAT must have Edit permissions on the affected pages.
import requests
from datetime import datetime
# Set the API endpoint URL
cdm_url = "http://localhost:8090/" # adjust to your environment
# Basic Auth credentials
username = "YOUR_USERNAME"
password = "YOUR_PASS"
# Page IDs returned by your SQL query
page_ids = ["6881282"]
# Properties to delete
properties = [
"comalaworkflowstasks",
"readack",
"comalaworkflowparameters",
"comalaworkflowstaskscurrentmodelversion"
]
# Prepare log file
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = f"delete_properties_log_{timestamp}.txt"
def log(message):
with open(log_file, "a", encoding="utf-8") as f:
f.write(message + "\n")
# Initial connection check
response = requests.get(cdm_url, auth=(username, password))
if response.status_code == 200:
for page_id in page_ids:
for prop in properties:
log("----------")
log(f"Processing property '{prop}' on Page ID: {page_id}")
# First check if the property exists
check_url = f"{cdm_url}rest/api/content/{page_id}/property/{prop}"
check_response = requests.get(check_url, auth=(username, password))
if check_response.status_code == 200:
# Property exists, now attempt delete
delete_response = requests.delete(check_url, auth=(username, password))
if delete_response.status_code == 204:
log(f"Success: Property '{prop}' deleted for {page_id}")
elif delete_response.status_code == 401:
log(f"Unauthorized (401): Invalid username or password for {page_id}")
elif delete_response.status_code == 403:
log(f"Forbidden (403): No permission to delete property '{prop}' on {page_id}")
elif delete_response.status_code == 409:
log(f"Conflict (409): Another update is in progress for {page_id}")
elif delete_response.status_code >= 500:
log(f"Server error ({delete_response.status_code}) on {page_id}")
else:
log(f"Unexpected status {delete_response.status_code} for {page_id}: {delete_response.text[:200]}")
elif check_response.status_code == 404:
log(f"Property '{prop}' does not exist on {page_id}")
elif check_response.status_code == 403:
log(f"Forbidden (403): No permission to view property '{prop}' on {page_id}")
elif check_response.status_code == 401:
log(f"Unauthorized (401): Invalid username or password for {page_id}")
else:
log(f"Unexpected status {check_response.status_code} while checking property '{prop}' on {page_id}: {check_response.text[:200]}")
else:
log(f"Error: Initial request failed with status code {response.status_code}")
print(f"Execution finished. Logs saved in {log_file}")