Repository: ansible/ansible

Issue:
Parsing error results in ERROR! Unexpected Exception: 0
##### ISSUE TYPE
- Bug Report
##### COMPONENT NAME

parsing/quoting.py
##### ANSIBLE VERSION

```
ansible 2.1.1.0
  config file = /home/lmadsen/src/github/leifmadsen/ansible-cira/ansible.cfg
  configured module search path = Default w/o overrides
```
##### CONFIGURATION

```
[defaults]
roles_path = ./roles
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ~/.ansible/cachedir
fact_caching_timeout = 86400
host_key_checking = False
inventory = ./hosts/

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=300s -o ForwardAgent=yes
```
##### OS / ENVIRONMENT

Fedora 24
##### SUMMARY

When failing to provide a `tasks:` value about a named play, `ERROR! Unexpected Exception: 0` is returned.
##### STEPS TO REPRODUCE

Fail to provide a `tasks:` header above a named play.

<!--- Paste example playbooks or commands between quotes below -->

```
# vim: ft=ansible                                                               
---                                                                             
- hosts: jenkins_master                                                         
  tags:                                                                         
    - jenkins_master                                                            
    - jenkins_ssh                                                               

  vars_files:                                                                   
    - "{{ inventory_dir  }}/../vars/main.yml"                                   
    - ~/.ansible/vars/cira_vars.yml                                             

    - name: Create Jenkins user .ssh directory                                  
      file:                                                                     
        path: "{{ jenkins_master_ssh_directory }}"                              
        state: directory                                                        
        owner: jenkins                                                          
        group: jenkins                                                          
        mode: "0700"
```

_Note_ the missing `tasks:` above `- name: Create...`

<!--- You can also paste gist.github.com links for larger files -->
##### EXPECTED RESULTS

<!--- What did you expect to happen when running the steps above? -->

Ansible to nicely exit pointing to line in file that couldn't be parsed appropriately.
##### ACTUAL RESULTS

<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

Ansible crashes and outputs information that isn't overly helpful to narrowing down where the parsing error happened in the file.

<!--- Paste verbatim command output between quotes below -->

```
ansible-playbook -vvv --syntax-check jenkins_ssh.yml 
Using /home/lmadsen/src/github/leifmadsen/ansible-cira/ansible.cfg as config file
1 plays in jenkins_ssh.yml
ERROR! Unexpected Exception: 0
the full traceback was:

Traceback (most recent call last):
  File "/usr/bin/ansible-playbook", line 86, in <module>
    sys.exit(cli.run())
  File "/home/lmadsen/.local/lib/python2.7/site-packages/ansible/cli/playbook.py", line 154, in run
    results = pbex.run()
  File "/home/lmadsen/.local/lib/python2.7/site-packages/ansible/executor/playbook_executor.py", line 117, in run
    all_vars = self._variable_manager.get_vars(loader=self._loader, play=play)
  File "/home/lmadsen/.local/lib/python2.7/site-packages/ansible/vars/__init__.py", line 305, in get_vars
    data = preprocess_vars(loader.load_from_file(vars_file))
  File "/home/lmadsen/.local/lib/python2.7/site-packages/ansible/parsing/dataloader.py", line 105, in load_from_file
    file_name = self.path_dwim(file_name)
  File "/home/lmadsen/.local/lib/python2.7/site-packages/ansible/parsing/dataloader.py", line 212, in path_dwim
    given = unquote(given)
  File "/home/lmadsen/.local/lib/python2.7/site-packages/ansible/parsing/quoting.py", line 28, in unquote
    if is_quoted(data):
  File "/home/lmadsen/.local/lib/python2.7/site-packages/ansible/parsing/quoting.py", line 24, in is_quoted
    return len(data) > 1 and data[0] == data[-1] and data[0] in ('"', "'") and data[-2] != '\\'
KeyError: 0
```



Discussion:
+1 
Ansible version: ansible 2.3.0.0



> 2017-05-16 16:49:35,593 p=2435 u=root |  META: ran handlers
> 2017-05-16 16:49:35,594 p=2435 u=root |  META: ran handlers
> 2017-05-16 16:49:35,601 p=2435 u=root |  ERROR! Unexpected Exception: 0
> 2017-05-16 16:49:35,603 p=2435 u=root |  the full traceback was:
> 
> Traceback (most recent call last):
>   File "/usr/local/bin/ansible-playbook", line 109, in <module>
>     exit_code = cli.run()
>   File "/usr/local/lib/python2.7/dist-packages/ansible/cli/playbook.py", line 154, in run
>     results = pbex.run()
>   File "/usr/local/lib/python2.7/dist-packages/ansible/executor/playbook_executor.py", line 125, in run
>     all_vars = self._variable_manager.get_vars(loader=self._loader, play=play)
>   File "/usr/local/lib/python2.7/dist-packages/ansible/vars/__init__.py", line 312, in get_vars
>     data = preprocess_vars(loader.load_from_file(vars_file))
>   File "/usr/local/lib/python2.7/dist-packages/ansible/parsing/dataloader.py", line 110, in load_from_file
>     file_name = self.path_dwim(file_name)
>   File "/usr/local/lib/python2.7/dist-packages/ansible/parsing/dataloader.py", line 218, in path_dwim
>     given = unquote(given)
>   File "/usr/local/lib/python2.7/dist-packages/ansible/parsing/quoting.py", line 28, in unquote
>     if is_quoted(data):
>   File "/usr/local/lib/python2.7/dist-packages/ansible/parsing/quoting.py", line 24, in is_quoted
>     return len(data) > 1 and data[0] == data[-1] and data[0] in ('"', "'") and data[-2] != '\\'
> KeyError: 0
+1 for ansible 2.3.0:

```
ERROR! Unexpected Exception: 0
the full traceback was:

Traceback (most recent call last):
  File ".venv/bin/ansible-playbook", line 109, in <module>
    exit_code = cli.run()
  File "/home/user/ansible-playbook/.venv/local/lib/python2.7/site-packages/ansible/cli/playbook.py", line 154, in run
    results = pbex.run()
  File "/home/user/ansible-playbook/.venv/local/lib/python2.7/site-packages/ansible/executor/playbook_executor.py", line 125, in run
    all_vars = self._variable_manager.get_vars(loader=self._loader, play=play)
  File "/home/user/ansible-playbook/.venv/local/lib/python2.7/site-packages/ansible/vars/__init__.py", line 312, in get_vars
    data = preprocess_vars(loader.load_from_file(vars_file))
  File "/home/user/ansible-playbook/.venv/local/lib/python2.7/site-packages/ansible/parsing/dataloader.py", line 110, in load_from_file
    file_name = self.path_dwim(file_name)
  File "/home/user/ansible-playbook/.venv/local/lib/python2.7/site-packages/ansible/parsing/dataloader.py", line 218, in path_dwim 
    given = unquote(given)
  File "/home/user/ansible-playbook/.venv/local/lib/python2.7/site-packages/ansible/parsing/quoting.py", line 28, in unquote
    if is_quoted(data):
  File "/home/user/ansible-playbook/.venv/local/lib/python2.7/site-packages/ansible/parsing/quoting.py", line 24, in is_quoted 
    return len(data) > 1 and data[0] == data[-1] and data[0] in ('"', "'") and data[-2] != '\\'
KeyError: 0
```

This occurs with the following code:

```
- hosts: localhost
  become: false
  gather_facts: false
  vars_files:
    - { var_file: 'group_vars/vagrant.yml', when: "groups['vagrant'] is not defined" }
```
@leifmadsen I'm unable to reproduce this with your example in Ansible 2.5.3, can you please verify this is resolved for you?
@kayrus what you're attempting to do is not supported, but we should definitely handle that with a graceful error message

Resolving patch:
diff --git a/lib/ansible/vars/manager.py b/lib/ansible/vars/manager.py
--- a/lib/ansible/vars/manager.py
+++ b/lib/ansible/vars/manager.py
@@ -22,7 +22,7 @@
 import os
 import sys
 
-from collections import defaultdict, MutableMapping
+from collections import defaultdict, MutableMapping, Sequence
 
 try:
     from hashlib import sha1
@@ -340,6 +340,12 @@ def plugins_by_groups():
                     try:
                         for vars_file in vars_file_list:
                             vars_file = templar.template(vars_file)
+                            if not (isinstance(vars_file, Sequence)):
+                                raise AnsibleError(
+                                    "Invalid vars_files entry found: %r\n"
+                                    "vars_files entries should be either a string type or "
+                                    "a list of string types after template expansion" % vars_file
+                                )
                             try:
                                 data = preprocess_vars(self._loader.load_from_file(vars_file, unsafe=True))
                                 if data is not None:


Test patch: