Copying pages with Comala Document Management fails with HTTP 500 error

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}")

Related Articles