Skip to content

fix(extensions): coerce non-mapping YAML config roots to {} in ConfigManager#3345

Open
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/config-manager-nonmapping-yaml
Open

fix(extensions): coerce non-mapping YAML config roots to {} in ConfigManager#3345
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/config-manager-nonmapping-yaml

Conversation

@jawwad-ali

Copy link
Copy Markdown
Contributor

Description

ConfigManager._load_yaml_config guards only falsy YAML roots:

return yaml.safe_load(file_path.read_text(encoding="utf-8")) or {}

A truthy non-mapping root — a YAML list (- foo) or a scalar (just a string) — passes straight through, then _merge_configs does .items() on it:

AttributeError: 'list' object has no attribute 'items'

reproduced on main @ bba473c via ConfigManager(root, 'jira').get_config() with a list-root jira-config.yml. has_value/get_value crash the same way, and — worse — HookExecutor.should_execute_hook wraps evaluation in a blanket except Exception: return False, so a malformed config file silently disables every config.* hook condition for that extension instead of surfacing an error.

Fix

Coerce a non-dict root (list/scalar, or None for an empty file) to {}, mirroring the existing non-dict-root guard already present in get_project_config(). One place hardens all three callers (_get_extension_defaults, _get_project_config, _get_local_config).

Testing

New TestConfigManagerNonMappingYaml (5 tests, all fail-before / pass-after, verified by source-stash):

  • list-root and scalar-root get_config() return {} instead of raising;
  • has_value/get_value don't raise;
  • a valid local-config.yml still layers over a malformed (list-root) project config;
  • HookExecutor._evaluate_condition('config.x is set', 'jira') returns False cleanly (asserted directly, since should_execute_hook masks the exception).

uvx ruff check clean.

AI Disclosure

  • I did use AI assistance (describe below)

Found and fixed with Claude Code (Claude Fable 5) under my direction. AI traced the crash from _load_yaml_config through _merge_configs into the swallowed hook exception; I reproduced it, verified fail-before/pass-after, and reviewed the diff before submitting.

…Manager

ConfigManager._load_yaml_config returned yaml.safe_load(...) or {}, which
only guards falsy roots — a truthy non-mapping root (a YAML list or
scalar) flows straight into _merge_configs, whose .items() raises
AttributeError. get_config()/has_value()/get_value() then crash, and via
should_execute_hook's blanket 'except Exception: return False' every
config-based hook condition for that extension is silently disabled.
Coerce a non-dict root to {}, mirroring the existing non-dict-root guard
in get_project_config(). Hardens all three call sites in one place.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jawwad-ali jawwad-ali requested a review from mnriem as a code owner July 5, 2026 14:29
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.

1 participant