Troubleshooting Puppet YAML errors

The problem

If, when running puppet, you see the following error, it is probably not directly a fault with the init.pp file it mentions in the error code, rather a problem with a hiera lookup referenced in that line (thus in this case a problem parsing YAML).

Error: Could not retrieve catalog from remote server: Error 400 on SERVER: malformed format string - %S at /data/puppet_config/environments/master/modules/apache/manifests/init.pp:4 on node server1.company.com
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run

[root@puppet puppetcode]# head -4 /data/puppet_config/environments/master/modules/apache/packages/init.pp

class apache {
 
 
        $apache = hiera("apacheversion")     # <-- Line 4
... 

That’s all well and good, but how do you possibly trace the error through the monstrous YAML collection in Puppet?

The solution

Log into the Puppet Master (or a Linux host with the Puppet repo checked out) and run the below command.
We can see from the this output that one YAML file is missing closing quotes, which results in a YAML syntax error.

[root@puppet puppetcode]# cd /data/puppet_config/environments/master/hieradata  # --> This directory will depend on the environment - this example looks at “master”
[root@puppet hieradata]# find . -name "*.yaml" -print0 | xargs -0rt -I ^ ruby -e "require 'yaml'; YAML.parse(File.open('^')) or puts('^')"
ruby -e require 'yaml'; YAML.parse(File.open('./primaryclass/webservers.yaml')) or puts('./primaryclass/webservers.yaml')
ruby -e require 'yaml'; YAML.parse(File.open('./primaryclass/base.yaml')) or puts('./primaryclass/base.yaml')
ruby -e require 'yaml'; YAML.parse(File.open('./primaryclass/devservers.yaml')) or puts('./primaryclass/devservers.yaml')
ruby -e require 'yaml'; YAML.parse(File.open('./nodes/dbserver1.yaml')) or puts('./nodes/dbserver1.yaml')
/usr/lib/ruby/1.8/yaml.rb:176:in `load': syntax error on line 74, col -1: `    eth0.101: (ArgumentError)
      type: 'vlan'
      ipaddress: '192.168.1.24'
      routes:
          - '192.168.4.0/24 via 192.168.1.1
    eth0.205:
      type: 'vlan'
      ipaddress: '192.168.44.24'
      routes:
          - '192.168.22.0/24 via 192.168.44.1'
'
        from /usr/lib/ruby/1.8/yaml.rb:176:in `parse'
        from -e:1
ruby -e require 'yaml'; YAML.parse(File.open('./nodes/dbserver2.yaml')) or puts('./nodes/dbserver2.yaml')

Note the first "routes" entry in the error output doesn't contain a closing quote

Prevention

When committing code, you can use lint checks such as these to confirm the code is valid. For instance, if checking out locally to edit the files, the simplest option is to run the same command before committing and pushing/merging.

You can also configure these checks as pre merge hooks in git etc.