Environment
-
WP-Discourse version: 2.5.7
-
Discourse version: 3.5.0.beta5-dev
-
WordPress version: 6.9
-
Hosting: WP Engine (WordPress multisite)
-
Discourse instance: Self-hosted / Discourse hosting (forum.avweb.com)
Summary
Posts published from WordPress successfully create topics on Discourse, but discourse_permalink is stored as an empty string in post meta. This causes all subsequent comment syncs to silently fail, as sync_comments() in discourse-comment.php bails early when the permalink is empty. The Discourse topic ID and webhook sync flag are written correctly — only the permalink is missing.
We identified 174 affected posts on a single site in our multisite network. The issue appears to have started around mid-December 2025.
Steps to Reproduce
-
Publish a WordPress post configured to create a Discourse topic
-
Topic is created successfully on Discourse
-
discourse_topic_idis saved to post meta ✓ -
wpdc_sync_post_commentsis set to1(webhook fires correctly) ✓ -
discourse_permalinkis saved as an empty string ✗ -
discourse_comments_rawis never written (sync never completes)
Expected Behavior
discourse_permalink should contain the full Discourse topic URL (e.g., The China Chickens Come Home - AVweb - News Discussion - AVweb.com Discussion ) after successful topic creation.
Actual Behavior
discourse_permalink exists as a meta key but its value is empty. Every subsequent call to sync_comments() hits this guard on line 209 of lib/discourse-comment.php and returns early:
if ( ! $discourse_permalink ) {
return 0;
}
Because the sync never completes, discourse_last_sync is never written, so the plugin retries on every page load — and fails every time.
Diagnosis
We traced the issue through the following code path:
-
DiscourseCommentFormatter::format()callsdo_action('wpdc_sync_discourse_comments')which triggersDiscourseComment::sync_comments() -
sync_comments()checks fordiscourse_permalink— finds it empty, returns 0 -
format()then checks fordiscourse_comments_rawin post custom — finds it missing, returnsbad_response_html() -
Comments never display for the affected post
The topic creation flow is writing discourse_topic_id but failing to persist the permalink. We were able to reconstruct the correct permalinks by querying the Discourse API at /t/{topic_id}.json and writing them back to post meta, which resolved the sync for all 174 posts.
Workaround
We wrote a WP-CLI repair script that:
-
Finds posts where
wpdc_sync_post_comments = 1butdiscourse_comments_rawis missing -
Fetches the topic slug from
/t/{topic_id}.json -
Reconstructs and saves the permalink
-
Fetches and saves comments from
/t/{slug}/{topic_id}/wordpress.json
We’re running this on a scheduled cron as a stopgap.
Additional Issues Found During Investigation
1. Global MySQL Lock Causes Silent Sync Failures
File: lib/discourse-comment.php, line 176
$got_lock = $wpdb->get_row( "SELECT GET_LOCK( 'discourse_lock', 0 ) got_it" );
sync_comments() uses a single global MySQL lock (discourse_lock) shared across all posts on the entire installation. The timeout is 0 (non-blocking), so if any post is currently syncing, all other posts silently skip their sync — no logging, no retry.
On a high-volume site publishing multiple posts per day, this creates a race condition where posts consistently lose the lock and never sync. Combined with the 10-minute sync period, a post can get permanently stuck if it loses the lock on its first few attempts and then the empty permalink issue prevents future syncs.
Suggested fix: Use a per-post lock:
$got_lock = $wpdb->get_row( "SELECT GET_LOCK( 'discourse_lock_{$post_id}', 0 ) got_it" );
With the corresponding release:
$wpdb->get_results( "SELECT RELEASE_LOCK( 'discourse_lock_{$post_id}' )" );
2. Double-Escaping of discourse_comments_raw
File: lib/discourse-comment.php, line 222
update_post_meta( $post_id, 'discourse_comments_raw', esc_sql( $raw_body ) );
update_post_meta() uses $wpdb->prepare() internally, so wrapping the value in esc_sql() causes double-escaping. Over multiple sync cycles, quotes in the JSON body accumulate escape characters (\" → \\\" → \\\\\\\") until the JSON becomes unparseable.
Suggested fix:
update_post_meta( $post_id, 'discourse_comments_raw', $raw_body );
Impact
These issues compound: the empty permalink prevents initial sync, the global lock prevents recovery, and the double-escaping can corrupt data for posts that do manage to sync. On our installation, 174 posts (roughly 2 months of content) were affected before we identified the root cause.