Skip to content

Commit 6e883ad

Browse files
authored
fix(confluence): handle missing body.storage.value in page retrieval (sooperset#767)
Self-hosted Confluence (especially older versions like 6.7.1) may return pages without body content, causing TypeError when accessing page["body"]["storage"]["value"]. This adds try/except handling with logging to gracefully handle None values in the body chain. Fixes sooperset#760 Github-Issue:sooperset#760
1 parent 85b7419 commit 6e883ad

File tree

2 files changed

+75
-3
lines changed

2 files changed

+75
-3
lines changed

src/mcp_atlassian/confluence/pages.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ def get_page_content(
6868
)
6969

7070
space_key = page.get("space", {}).get("key", "")
71-
content = page["body"]["storage"]["value"]
71+
try:
72+
content = page["body"]["storage"]["value"]
73+
except (KeyError, TypeError) as e:
74+
logger.warning(
75+
f"Page {page.get('id', 'unknown')} missing body.storage.value: {e}"
76+
)
77+
content = ""
7278
processed_html, processed_markdown = self.preprocessor.process_html_content(
7379
content, space_key=space_key, confluence_client=self.confluence
7480
)
@@ -183,7 +189,13 @@ def get_page_by_title(
183189
)
184190
return None
185191

186-
content = page["body"]["storage"]["value"]
192+
try:
193+
content = page["body"]["storage"]["value"]
194+
except (KeyError, TypeError) as e:
195+
logger.warning(
196+
f"Page {page.get('id', 'unknown')} missing body.storage.value: {e}"
197+
)
198+
content = ""
187199
processed_html, processed_markdown = self.preprocessor.process_html_content(
188200
content, space_key=space_key, confluence_client=self.confluence
189201
)
@@ -244,7 +256,13 @@ def get_space_pages(
244256

245257
page_models = []
246258
for page in pages:
247-
content = page["body"]["storage"]["value"]
259+
try:
260+
content = page["body"]["storage"]["value"]
261+
except (KeyError, TypeError) as e:
262+
logger.warning(
263+
f"Page {page.get('id', 'unknown')} missing body.storage.value: {e}"
264+
)
265+
content = ""
248266
processed_html, processed_markdown = self.preprocessor.process_html_content(
249267
content, space_key=space_key, confluence_client=self.confluence
250268
)

tests/unit/confluence/test_pages.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,60 @@ def test_non_oauth_still_uses_v1_api(self, pages_mixin):
845845
assert result.id == "v1_123456789"
846846
assert result.title == title
847847

848+
@pytest.mark.parametrize(
849+
"body",
850+
[None, {"storage": None}, {"storage": {"value": None}}],
851+
ids=["body=None", "storage=None", "value=None"],
852+
)
853+
def test_get_page_content_missing_body_regression(self, pages_mixin, body):
854+
"""Regression test for #760: handle missing body.storage.value."""
855+
pages_mixin.confluence.get_page_by_id.return_value = {
856+
"id": "123456",
857+
"title": "Test",
858+
"space": {"key": "TEST"},
859+
"body": body,
860+
"version": {"number": 1},
861+
}
862+
pages_mixin.config.url = "https://example.atlassian.net/wiki"
863+
result = pages_mixin.get_page_content("123456")
864+
assert isinstance(result, ConfluencePage)
865+
assert result.id == "123456"
866+
867+
def test_get_page_by_title_missing_body_regression(self, pages_mixin):
868+
"""Regression test for #760: get_page_by_title handles None body."""
869+
pages_mixin.confluence.get_page_by_title.return_value = {
870+
"id": "123456",
871+
"title": "Test",
872+
"space": {"key": "TEST"},
873+
"body": None,
874+
"version": {"number": 1},
875+
}
876+
pages_mixin.config.url = "https://example.atlassian.net/wiki"
877+
result = pages_mixin.get_page_by_title("TEST", "Test")
878+
assert isinstance(result, ConfluencePage)
879+
880+
def test_get_space_pages_missing_body_regression(self, pages_mixin):
881+
"""Regression test for #760: get_space_pages handles None body."""
882+
pages_mixin.confluence.get_all_pages_from_space.return_value = [
883+
{
884+
"id": "1",
885+
"title": "A",
886+
"space": {"key": "T"},
887+
"body": None,
888+
"version": {"number": 1},
889+
},
890+
{
891+
"id": "2",
892+
"title": "B",
893+
"space": {"key": "T"},
894+
"body": {"storage": None},
895+
"version": {"number": 1},
896+
},
897+
]
898+
pages_mixin.config.url = "https://example.atlassian.net/wiki"
899+
results = pages_mixin.get_space_pages("T")
900+
assert len(results) == 2
901+
848902

849903
class TestPagesOAuthMixin:
850904
"""Tests for PagesMixin with OAuth authentication."""

0 commit comments

Comments
 (0)