Skip to content

Fix AttributeError when node.id is accessed on non-Node objects#911

Open
veeceey wants to merge 1 commit intoyaml:mainfrom
veeceey:fix/issue-907-attributeerror-node-id
Open

Fix AttributeError when node.id is accessed on non-Node objects#911
veeceey wants to merge 1 commit intoyaml:mainfrom
veeceey:fix/issue-907-attributeerror-node-id

Conversation

@veeceey
Copy link

@veeceey veeceey commented Feb 13, 2026

Summary

Fixes #907 by safely accessing node.id and node.start_mark attributes in error handling code.

Problem

When constructor methods like construct_scalar(), construct_sequence(), etc. receive a non-Node object (e.g., None, a type object, or any arbitrary Python object), the error handling code unconditionally accesses node.id and node.start_mark, which causes an AttributeError before the intended ConstructorError can be raised.

Solution

Use getattr() to safely access these attributes:

  • For node.id: fall back to type(node).__name__ to still provide useful type information in the error message
  • For node.start_mark: fall back to None since position information isn't available for non-Node objects

Testing

Tested with the reproduction case from the issue:

import yaml

class Null:
    pass

obj = yaml.loader.SafeLoader("")
ret = obj.construct_yaml_float(Null)

Before: AttributeError: type object 'Null' has no attribute 'id'
After: ConstructorError: expected a scalar node, but found type

Also verified that normal YAML parsing continues to work correctly.

When construct_scalar, construct_sequence, construct_mapping, construct_pairs,
construct_yaml_omap, or construct_yaml_pairs receive a non-Node object, the
error handling code would fail with AttributeError when trying to access
node.id or node.start_mark attributes.

This fix uses getattr() to safely access these attributes, falling back to
type(node).__name__ for the id and None for start_mark when the attributes
don't exist.

Fixes yaml#907
@veeceey
Copy link
Author

veeceey commented Feb 13, 2026

Manual Test Results

I've verified the fix works correctly with various test cases:

Test 1: Original reproduction case (Null type)

import yaml

class Null:
    pass

obj = yaml.loader.SafeLoader("")
ret = obj.construct_yaml_float(Null)

Result: ✅ ConstructorError: expected a scalar node, but found type (previously raised AttributeError)

Test 2: None object

obj = yaml.loader.SafeLoader("")
ret = obj.construct_scalar(None)

Result: ✅ ConstructorError: expected a scalar node, but found NoneType

Test 3: String instead of SequenceNode

obj = yaml.loader.SafeLoader("")
ret = obj.construct_sequence("not a node")

Result: ✅ ConstructorError: expected a sequence node, but found str

Test 4: Custom object instead of MappingNode

class CustomObject:
    pass

obj = yaml.loader.SafeLoader("")
ret = obj.construct_mapping(CustomObject())

Result: ✅ ConstructorError: expected a mapping node, but found CustomObject

Test 5: Integer instead of MappingNode

obj = yaml.loader.SafeLoader("")
ret = obj.construct_pairs(42)

Result: ✅ ConstructorError: expected a mapping node, but found int

Test 6: Normal YAML parsing

data = yaml.safe_load("key: value")

Result: ✅ Works correctly, returns {'key': 'value'}

All tests pass. The fix ensures proper ConstructorError exceptions are raised instead of AttributeError when invalid objects are passed to constructor methods, while maintaining backward compatibility for normal YAML parsing.

@veeceey
Copy link
Author

veeceey commented Feb 19, 2026

Friendly ping - any chance someone could take a look at this when they get a chance? Happy to make any changes if needed.

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.

ConstructorError handler accesses node.id causing AttributeError

1 participant

Comments