Repository navigation
Inline citations delete the line breaks that follow them, collapsing paragraphs into the previous block #1289
Description
Issue
Inline document citations in assistant chat messages delete the whitespace that follows them. The citation link itself renders correctly, but the text that comes after the citation is jammed onto the end of the closing ) instead of starting a new paragraph, list item, or sentence. This affects every user reading a cited answer in chat.
Steps to Reproduce
-
Ask a question in a workspace that returns document citations (personal, group, or public).
-
Have the model answer with a citation followed by a blank line and then more prose, which is its normal output shape:
... used to support cited answers in chat. (Source: application_workflows.md, Page: 1) [#181b54f7-fcf9-479b-a58b-81a5da3ba251_1] Admins can configure the extraction approach for images and PDFs under **Admin Settings > Search & Extract**. -
Look at the rendered assistant message.
Expected Behavior
The paragraph after the citation renders as its own block, exactly as the model wrote it:
... used to support cited answers in chat. (Source: application_workflows.md, Page: 1)
Admins can configure the extraction approach for images and PDFs under Admin Settings > Search & Extract.
Actual Behavior
The blank line is gone and the following paragraph is absorbed into the line — and, when the citation is inside a numbered list, into the list item:
- Grounded chat: ... used to support cited answers in chat. (Source: application_workflows.md, Page: 1)Admins can configure the extraction approach for images and PDFs under Admin Settings > Search & Extract. The available modes are:
Other observed forms in the same message:
...warrants it (Source: document-intelligence.md, Page: 1)For best results, upload clear, readable images....(Source: uploading_documents.md, Page: 1)Thank you, Paul.
Impact
- Message structure is visibly wrong: paragraphs collapse into the preceding list item, headings and bullets shift, and sentences run together with no space after
). - The damage happens before markdown parsing, so it changes how the whole remainder of the block is interpreted, not just the characters next to the citation.
- Copied and exported message markdown is built from the same parsed output, so it inherits the lost line breaks.
- Content is still readable, so this is a presentation defect rather than a data-loss or security issue.
Notes
Suspected root cause is in parseCitations() in application/single_app/static/js/chat/chat-citations.js:
const citationRegex = /\(Source:\s*(...),\s*(Page(?:s)?|Sheet(?:s)?|Location):\s*(...)\)\s*((?:\[#.*?\]\s*)+)/gi;The trailing \s* inside the repeated bracket group is greedy and matches newlines, so it consumes the whitespace after the [#citation-id] marker. The replacement callback returns only the rebuilt (Source: ...) string and never re-emits that whitespace.
parseCitations() runs on raw markdown before marked.parse() (chat-messages.js, renderAiMessageContent()), so a swallowed \n\n changes markdown block parsing and the following paragraph becomes a continuation of the current block.
The cleanup pass a few lines below has the same class of defect in the other direction — its leading \s* can consume the newlines before a leftover [#guid] bracket that starts its own paragraph.
Reproduced against the production regex in isolation:
Input : "... in chat. (Source: application_workflows.md, Page: 1) [#181b54f7-..._1]\n\nAdmins can configure ..."
Output: "... in chat. (Source: application_workflows.md, Page: 1)Admins can configure ..."
Fix direction: preserve the trailing whitespace captured by the bracket group and re-emit it, and restrict the cleanup pass to horizontal whitespace so line structure survives. Server-side functions_citation_tracking.py is not implicated — it matches with a non-consuming lookahead.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsReleased
Activity
paullizer commentedon Aug 19, 2026
Fixed on branch
paullizer-fix-citation-whitespace-collapse(v0.250.228).What changed
parseCitations()inchat-citations.jsnow captures the trailing whitespace off the matched[#citation-id]bracket group and re-emits it, so the blank line separating a citation from the next markdown block survives. Whitespace is restored exactly as the model emitted it, so a citation followed immediately by punctuation renders byte-identically to before.The leftover
[#guid]cleanup pass had the same defect in reverse via its leading greedy\s*. It is now three ordered passes: a bracket run occupying a whole line is removed with its line, a run opening a line is removed with its trailing spacing, and an inline run consumes only spaces and tabs.These are whitespace-only changes. The emitted citation HTML is unchanged, so escaping, sanitization, and the XSS surface are untouched.
Validation
functional_tests/test_chat_citation_whitespace_preservation.pyexecutes the realparseCitations()in a Node sandbox, then renders the result with the vendoredmarkedbundle, so it asserts user-visible block structure rather than regex output. 4/4 pass. All four fail against the pre-fix source, and each half of the fix was reverted independently to confirm both are covered.ui_tests/test_chat_citation_paragraph_spacing.pyseeds the reported message into the chat page and asserts the DOM: the numbered list item ends at its citation, the following text renders as its own<p>, and no sentence collides with a citation's closing parenthesis. Env-gated like the other UI tests.test_agent_document_search_citations.py,test_markdown_citation_lookup_fallback.py,test_chat_cited_source_tracking.py,test_unicode_table_conversion.py,test_comprehensive_table_support.py,test_stored_xss_chat_workspace_rendering_fix.py,test_external_links_new_window.py,test_azure_maps_tile_token_refresh_fix.py.[^\]]*\]cost is unchanged) and probed CRLF, fenced code blocks, tables, blockquotes, headings, and list markers against the^/manchors. Every case where the new cleanup differs from the old is strictly less destructive — notably the old pass could break a fenced code block by pulling its closing fence onto the previous line.Before / after
Rendered HTML for the reported message, before:
After:
Side effects worth noting
copyMarkdownis built from the same parsed output, so copied and exported message text keeps its line breaks too.Docs
docs/explanation/fixes/CHAT_CITATION_WHITESPACE_COLLAPSE_FIX.mdNo unresolved risks or follow-ups.
Preserve whitespace after inline document citations
paullizer commentedon Aug 19, 2026
PR: #1291 — open against
Development, all 12 checks passing, merge state clean.One correction to my earlier comment: #1290 landed while this was in flight and had already claimed v0.250.228, so this fix rebased onto
Developmentand ships as v0.250.229 instead. The release notes conflict was resolved by moving this entry into a new v0.250.229 section above the figure-chunk entry; the diff againstDevelopmentis 7 files, all belonging to this fix.paullizer commentedon Aug 19, 2026
Shipped in v0.250.229 via #1291.
Release note entries:
Full notes: https://microsoft.github.io/simplechat/explanation/release_notes/
Closing as completed. This issue stayed open after the fix merged because pull requests target
Developmentwhile the repository default branch ismain, so GitHub never applied theFixes #keyword automatically.Link release note issue references and archive pre-v0.230.001 notes