# Buggy code
def construct_mapping(self, node, deep=False):
    # Most of this is from yaml.constructor.SafeConstructor.  We replicate
    # it here so that we can warn users when they have duplicate dict keys
    # (pyyaml silently allows overwriting keys)
    if not isinstance(node, MappingNode):
        raise ConstructorError(
            None,
            None,
            "expected a mapping node, but found %s" % node.id,
            node.start_mark,
        )
    self.flatten_mapping(node)
    mapping = AnsibleMapping()

    # Add our extra information to the returned value
    mapping.ansible_pos = self._node_position_info(node)

    for key_node, value_node in node.value:
        key = self.construct_object(key_node, deep=deep)
        try:
            hash(key)
        except TypeError as exc:
            raise ConstructorError(
                "while constructing a mapping",
                node.start_mark,
                "found unacceptable key (%s)" % exc,
                key_node.start_mark,
            )

        if key in mapping:
            msg = (
                "While constructing a mapping from {1}, line {2}, column {3}, found a duplicate dict key ({0})."
                " Using last defined value only.".format(key, *mapping.ansible_pos)
            )
            if C.DUPLICATE_YAML_DICT_KEY == "warn":
                display.warning(msg)
            elif C.DUPLICATE_YAML_DICT_KEY == "error":
                raise ConstructorError(to_native(msg))
            else:
                # when 'ignore'
                display.debug(msg)

        value = self.construct_object(value_node, deep=deep)
        mapping[key] = value

    return mapping


# Traceback (json.decoder.JSONDecodeError)
ANSIBLE_DUPLICATE_YAML_DICT_KEY=error AWS_PROFILE=build ansible-playbook -vvv -i local, playbook.yml
ansible-playbook 2.9.1
config file = None
configured module search path = ['/Users/mtodor/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible
executable location = /usr/local/bin/ansible-playbook
python version = 3.7.5 (default, Nov  1 2019, 02:16:32) [Clang 11.0.0 (clang-1100.0.33.8)]
No config file found; using defaults
Parsed local, inventory source with host_list plugin

PLAYBOOK: playbook.yml *****************************************************************************************************************************************************************************************************************************************************
1 plays in playbook.yml
ERROR! Unexpected Exception, this is probably a bug: 'NoneType' object has no attribute 'line'
the full traceback was:

Traceback (most recent call last):
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/parsing/utils/yaml.py", line 70, in from_yaml
new_data = json.loads(data, cls=AnsibleJSONDecoder)
File "/usr/local/opt/python/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/__init__.py", line 361, in loads
return cls(**kw).decode(s)
File "/usr/local/opt/python/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/local/opt/python/Frameworks/Python.framework/Versions/3.7/lib/python3.7/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/parsing/utils/yaml.py", line 74, in from_yaml
new_data = _safe_load(data, file_name=file_name, vault_secrets=vault_secrets)
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/parsing/utils/yaml.py", line 49, in _safe_load
return loader.get_single_data()
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/yaml/constructor.py", line 43, in get_single_data
return self.construct_document(node)
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/yaml/constructor.py", line 52, in construct_document
for dummy in generator:
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/parsing/yaml/constructor.py", line 47, in construct_yaml_map
value = self.construct_mapping(node)
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/parsing/yaml/constructor.py", line 79, in construct_mapping
raise ConstructorError(to_native(msg))
yaml.constructor.ConstructorError: While constructing a mapping from /Users/mtodor/vars.yml, line 7, column 5, found a duplicate dict key (ID). Using last defined value only.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/usr/local/bin/ansible-playbook", line 123, in <module>
exit_code = cli.run()
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/cli/playbook.py", line 127, in run
results = pbex.run()
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/executor/playbook_executor.py", line 116, in run
all_vars = self._variable_manager.get_vars(play=play)
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/vars/manager.py", line 349, in get_vars
data = preprocess_vars(self._loader.load_from_file(vars_file, unsafe=True))
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/parsing/dataloader.py", line 89, in load_from_file
parsed_data = self.load(data=file_data, file_name=file_name, show_content=show_content)
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/parsing/dataloader.py", line 72, in load
return from_yaml(data, file_name, show_content, self._vault.secrets)
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/parsing/utils/yaml.py", line 76, in from_yaml
_handle_error(yaml_exc, file_name, show_content)
File "/usr/local/Cellar/ansible/2.9.1/libexec/lib/python3.7/site-packages/ansible/parsing/utils/yaml.py", line 37, in _handle_error
err_obj.ansible_pos = (file_name, yaml_exc.problem_mark.line + 1, yaml_exc.problem_mark.column + 1)
AttributeError: 'NoneType' object has no attribute 'line'

# Fixed code
def construct_mapping(self, node, deep=False):
    # Most of this is from yaml.constructor.SafeConstructor.  We replicate
    # it here so that we can warn users when they have duplicate dict keys
    # (pyyaml silently allows overwriting keys)
    if not isinstance(node, MappingNode):
        raise ConstructorError(
            None,
            None,
            "expected a mapping node, but found %s" % node.id,
            node.start_mark,
        )
    self.flatten_mapping(node)
    mapping = AnsibleMapping()

    # Add our extra information to the returned value
    mapping.ansible_pos = self._node_position_info(node)

    for key_node, value_node in node.value:
        key = self.construct_object(key_node, deep=deep)
        try:
            hash(key)
        except TypeError as exc:
            raise ConstructorError(
                "while constructing a mapping",
                node.start_mark,
                "found unacceptable key (%s)" % exc,
                key_node.start_mark,
            )

        if key in mapping:
            msg = (
                "While constructing a mapping from {1}, line {2}, column {3}, found a duplicate dict key ({0})."
                " Using last defined value only.".format(key, *mapping.ansible_pos)
            )
            if C.DUPLICATE_YAML_DICT_KEY == "warn":
                display.warning(msg)
            elif C.DUPLICATE_YAML_DICT_KEY == "error":
                raise ConstructorError(
                    context=None,
                    context_mark=None,
                    problem=to_native(msg),
                    problem_mark=node.start_mark,
                    note=None,
                )
            else:
                # when 'ignore'
                display.debug(msg)

        value = self.construct_object(value_node, deep=deep)
        mapping[key] = value

    return mapping