From 79381a4c5ff3cd1a2470b927dc3429c1c87c88c5 Mon Sep 17 00:00:00 2001 From: Pranav Date: Tue, 30 Dec 2025 11:25:54 -0800 Subject: [PATCH] fix: Add code_block method to WhatsApp and Instagram markdown renderers (#13166) Problem: SystemStackError: stack level too deep occurred when rendering messages with indented content (4+ spaces) for WhatsApp, Instagram, and Facebook channels. Root Cause: CommonMarker::Renderer#code_block contains a self-recursive placeholder that must be overridden: ``` def code_block(node) code_block(node) # calls itself infinitely end ``` WhatsAppRenderer and InstagramRenderer were missing this override, causing infinite recursion when markdown with 4-space indentation (interpreted as code blocks) was rendered. Fix: Added code_block method to both renderers that outputs the node content as plain text: ``` def code_block(node) out(node.string_content) end ``` Fix https://linear.app/chatwoot/issue/CW-6217/systemstackerror-stack-level-too-deep-systemstackerror --- .../markdown_renderers/instagram_renderer.rb | 4 ++ .../markdown_renderers/whats_app_renderer.rb | 4 ++ .../markdown_renderer_service_spec.rb | 48 +++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/app/services/messages/markdown_renderers/instagram_renderer.rb b/app/services/messages/markdown_renderers/instagram_renderer.rb index 3b47577b2..4ceb6f38e 100644 --- a/app/services/messages/markdown_renderers/instagram_renderer.rb +++ b/app/services/messages/markdown_renderers/instagram_renderer.rb @@ -42,6 +42,10 @@ class Messages::MarkdownRenderers::InstagramRenderer < Messages::MarkdownRendere cr end + def code_block(node) + out(node.string_content) + end + def softbreak(_node) out("\n") end diff --git a/app/services/messages/markdown_renderers/whats_app_renderer.rb b/app/services/messages/markdown_renderers/whats_app_renderer.rb index 9f7dbab6e..8f98218cf 100644 --- a/app/services/messages/markdown_renderers/whats_app_renderer.rb +++ b/app/services/messages/markdown_renderers/whats_app_renderer.rb @@ -30,6 +30,10 @@ class Messages::MarkdownRenderers::WhatsAppRenderer < Messages::MarkdownRenderer cr end + def code_block(node) + out(node.string_content) + end + def softbreak(_node) out("\n") end diff --git a/spec/services/messages/markdown_renderer_service_spec.rb b/spec/services/messages/markdown_renderer_service_spec.rb index 5b8e02e2f..e43b0d6d0 100644 --- a/spec/services/messages/markdown_renderer_service_spec.rb +++ b/spec/services/messages/markdown_renderer_service_spec.rb @@ -74,6 +74,24 @@ RSpec.describe Messages::MarkdownRendererService, type: :service do expect(result.scan("\n").count).to eq(4) expect(result).to include("Para 1\n\n\n\nPara 2") end + + it 'renders code blocks as plain text' do + content = "```\ncode here\n```" + result = described_class.new(content, channel_type).render + expect(result.strip).to eq('code here') + end + + it 'renders indented code blocks as plain text preserving exact content' do + content = ' indented code line' + result = described_class.new(content, channel_type).render + expect(result).to eq('indented code line') + end + + it 'handles code blocks with emojis and special characters without stack overflow' do + content = " first line\n 🌐 second line\n" + result = described_class.new(content, channel_type).render + expect(result).to eq("first line\n🌐 second line") + end end context 'when channel is Channel::Instagram' do @@ -130,6 +148,24 @@ RSpec.describe Messages::MarkdownRendererService, type: :service do expect(result.scan("\n").count).to eq(4) expect(result).to include("Para 1\n\n\n\nPara 2") end + + it 'renders code blocks as plain text' do + content = "```\ncode here\n```" + result = described_class.new(content, channel_type).render + expect(result.strip).to eq('code here') + end + + it 'renders indented code blocks as plain text preserving exact content' do + content = ' indented code line' + result = described_class.new(content, channel_type).render + expect(result).to eq('indented code line') + end + + it 'handles code blocks with emojis and special characters without stack overflow' do + content = " first line\n 🌐 second line\n" + result = described_class.new(content, channel_type).render + expect(result).to eq("first line\n🌐 second line") + end end context 'when channel is Channel::Line' do @@ -358,6 +394,18 @@ RSpec.describe Messages::MarkdownRendererService, type: :service do expect(result).to include('1. first step') expect(result).to include('2. second step') end + + it 'renders code blocks as plain text' do + content = "```\ncode here\n```" + result = described_class.new(content, channel_type).render + expect(result.strip).to eq('code here') + end + + it 'handles code blocks with emojis and special characters without stack overflow' do + content = " first line\n 🌐 second line\n" + result = described_class.new(content, channel_type).render + expect(result).to eq("first line\n🌐 second line") + end end context 'when channel is Channel::TwilioSms' do