Skip to content

Handle None content in OpenAI representation (#2353)#2525

Open
lntutor wants to merge 1 commit into
MaartenGr:masterfrom
lntutor:fix/openai-representation-none-content-2353
Open

Handle None content in OpenAI representation (#2353)#2525
lntutor wants to merge 1 commit into
MaartenGr:masterfrom
lntutor:fix/openai-representation-none-content-2353

Conversation

@lntutor

@lntutor lntutor commented Jul 26, 2026

Copy link
Copy Markdown

What does this PR do?

Fixes #2353

Root cause

In bertopic/representation/_openai.py, the generated label was guarded with:

if response and hasattr(response.choices[0].message, "content"):
    label = response.choices[0].message.content.strip().replace("topic: ", "")
else:
    label = "No label returned"

content is always present as a field on the message, so hasattr(...) is always True. When OpenAI/AzureOpenAI blocks a response via the content filter, it returns finish_reason='content_filter' with content=None. The guard passes and None.strip() raises:

AttributeError: 'NoneType' object has no attribute 'strip'

as reported in the issue traceback (at the .content.strip() line).

Fix

Read content first and check it explicitly against None:

content = response.choices[0].message.content if response else None
if content is not None:
    label = content.strip().replace("topic: ", "")
else:
    label = "No label returned"

This reuses the existing "No label returned" placeholder and keeps behavior identical for normal (non-None) responses. There is a single chat-completions code path in this file, so no other call site needs changing.

Test

Adds tests/test_representation/test_openai.py with two lightweight unit tests that call extract_topics directly against a mocked OpenAI client (no model training):

  • content=None returns {0: [("No label returned", 1)]} and does not raise (this fails on the previous implementation with the AttributeError from the issue).
  • normal content "topic: Pets\n" is stripped and the topic: prefix removed, yielding {0: [("Pets", 1)]}.

The module imports openai at load time, so the tests use pytest.importorskip("openai"), and openai>=1.0.0 is added to the test extra so they run under CI's uv sync --extra test.

Before submitting

The OpenAI/AzureOpenAI representation guarded the response with
`hasattr(response.choices[0].message, "content")` before calling
`.content.strip()`. The `content` field is always present, so `hasattr`
is always True, but its value can be `None` (e.g. when the response is
blocked by the content filter and `finish_reason='content_filter'`).
This raised `AttributeError: 'NoneType' object has no attribute 'strip'`.

Read `content` first and check it against `None` explicitly, falling back
to the existing "No label returned" placeholder. Normal (non-None)
responses keep their current behavior.

Add unit tests that drive `extract_topics` with a mocked OpenAI client
returning `content=None` (asserting the fallback label) and normal
content (asserting stripping and prefix removal). The tests use
`pytest.importorskip("openai")`, and `openai` is added to the test extra
so they run in CI.

Fixes MaartenGr#2353

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G8b9dEaJtpvydzz4ttM7JH
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AttributeError: 'NoneType' object has no attribute 'strip' when using AzureOpenAI client

1 participant