Skip to content

fix(bedrock): guard against null url in image_url parts and lifecycle status (#55686) - #55996

Open
AlexFucuson9 wants to merge 1 commit into
NousResearch:mainfrom
AlexFucuson9:fix/bedrock-null-url-guard
Open

fix(bedrock): guard against null url in image_url parts and lifecycle status (#55686)#55996
AlexFucuson9 wants to merge 1 commit into
NousResearch:mainfrom
AlexFucuson9:fix/bedrock-null-url-guard

Conversation

@AlexFucuson9

Copy link
Copy Markdown
Contributor

Summary

Prevent AttributeError crash when Bedrock Converse encounters an image_url part with a null url field, or a model lifecycle entry with a null status.

Problem

Two .get(key, "") patterns crash when the key exists with value None:

  1. image_url.get("url", "") returns None (not "") when url is explicitly null → url.startswith("data:") raises AttributeError: 'NoneType' object has no attribute 'startswith'

  2. lifecycle.get("status", "").upper() returns None when status is null → .upper() raises AttributeError

The dict.get(key, default) default only applies when the key is absent, not when it is present with value None.

Fix

Use (x.get("key") or "") which coalesces both absent-key and None-value to empty string:

  • image_url.get("url") or "" — null URL parts are safely skipped (empty string doesn't match data: prefix)
  • lifecycle.get("status") or "" — null status is treated as non-ACTIVE (model skipped)

Fixes #55686

… status

Two .get(key, "") patterns crash when the key exists with value None
(the default only applies when the key is ABSENT, not when it is None):

1. image_url.get("url", "") → None when url is null →
   url.startswith("data:") raises AttributeError

2. lifecycle.get("status", "").upper() → None when status is null →
   .upper() raises AttributeError

Fix: use (x.get("key") or "") which coalesces both absent-key and
None-value to empty string.

Fixes NousResearch#55686
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/bedrock AWS Bedrock (boto3, IAM) P2 Medium — degraded but workaround exists labels Jul 1, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related: this fixes the same image_url null-url crash (#55686) as the earlier open PRs #55685 (earliest, has regression tests) and #55699, and additionally guards a null modelLifecycle.status in discover_bedrock_models. It's a superset of the null-url fix, not a duplicate. Maintainer to pick the canonical PR for the shared image_url fix (#55685 looks canonical: earliest + regression tests) and decide whether to fold in this PR's extra lifecycle-status guard.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the focused Bedrock hardening. The stated null cases remain reproducible from current main: agent/bedrock_adapter.py:522-523 can call startswith on a null URL, and agent/bedrock_adapter.py:1147-1148 can call upper on a null lifecycle status.

Problems

  • The PR changes only agent/bedrock_adapter.py; it adds no regression coverage. Existing tests cover a valid image data URL at tests/agent/test_bedrock_adapter.py:332-348 and ACTIVE/LEGACY lifecycle states at :711-773, not either null case.
  • The proposed or "" expressions fix None, but truthy non-string values can still reach startswith or upper. The analogous Gemini converter uses an explicit string guard at agent/gemini_native_adapter.py:213-215.

Suggested changes

  • Add direct null-URL and null-lifecycle-status regression tests in tests/agent/test_bedrock_adapter.py.
  • Use explicit string normalization for both values before their string-method calls.

Automated hermes-sweeper review.

Comment thread agent/bedrock_adapter.py
elif part_type == "image_url":
image_url = part.get("image_url", {})
url = image_url.get("url", "") if isinstance(image_url, dict) else ""
url = (image_url.get("url") or "") if isinstance(image_url, dict) else ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes None, but a truthy non-string value such as 123 still reaches url.startswith(...) below. Please normalize with an explicit isinstance(url, str) guard, matching the defensive Gemini converter.

Comment thread agent/bedrock_adapter.py
# Only include active, streaming-capable, text-output models
lifecycle = summary.get("modelLifecycle", {})
if lifecycle.get("status", "").upper() != "ACTIVE":
if (lifecycle.get("status") or "").upper() != "ACTIVE":

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This coalesces null status values, but truthy non-strings still reach .upper(). Normalize to a string before comparison so malformed lifecycle payloads cannot retain the same crash class.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/bedrock AWS Bedrock (boto3, IAM) sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bedrock Converse conversion crashes (AttributeError) on an image_url part with a null url

3 participants