puppetlabs/puppet · error · Puppet::Error

Can not create #{@resource.title}; #{dirname} is not a direc

Error message

Can not create #{@resource.title}; #{dirname} is not a directory

What it means

Second branch of ensure#check: the parent path exists but FileTest.directory? is false — a regular file, device, or file-symlink occupies the dirname slot, so no child can be created beneath it and Puppet raises '<title>; <dirname> is not a directory'.

Source

Thrown at lib/puppet/type/file/ensure.rb:155

      end
      if should == :absent
        is = property.retrieve
      else
        is = :absent
      end

      property.change_to_s(is, should)
    end

    # Check that we can actually create anything
    def check
      basedir = File.dirname(@resource[:path])

      if !Puppet::FileSystem.exist?(basedir)
        raise Puppet::Error,
              "Can not create #{@resource.title}; parent directory does not exist"
      elsif !FileTest.directory?(basedir)
        raise Puppet::Error,
              "Can not create #{@resource.title}; #{dirname} is not a directory"
      end
    end

    # We have to treat :present specially, because it works with any
    # type of file.
    def insync?(currentvalue)
      unless currentvalue == :absent or resource.replace?
        return true
      end

      if should == :present
        !(currentvalue.nil? or currentvalue == :absent)
      else
        super(currentvalue)
      end
    end

View on GitHub (pinned to e227c27540)

Solutions

  1. Inspect the parent: `ls -ld <dirname>` to identify the conflicting object.
  2. If it is a stray file, remove it (or manage its removal with a file resource) so the directory can be created.
  3. Manage the parent explicitly as ensure => directory so Puppet converges it before children.
  4. Fix the manifest if the child path was meant to nest elsewhere.

Example fix

// before: /etc/app/conf.d is a regular file on disk
file { '/etc/app/conf.d/routes.conf':
  ensure => file,
  source => 'puppet:///modules/app/routes.conf',
}

// after: converge the parent first (remove the stray file once)
file { '/etc/app/conf.d': ensure => directory }
->
file { '/etc/app/conf.d/routes.conf':
  ensure => file,
  source => 'puppet:///modules/app/routes.conf',
}
Defensive patterns

Strategy: validation

Validate before calling

# Ruby pre-flight
parent = File.dirname(path)
if File.exist?(parent) && !File.directory?(parent)
  fail("#{parent} exists but is a #{File.ftype(parent)}, not a directory — remove or relocate it")
end

Try / catch

rescue Puppet::Error; on /is not a directory/ lstat the parent path, remove or relocate the conflicting object (or manage it as ensure => absent), then re-run the agent.

Prevention

When it happens

Trigger: `file { '/etc/app/conf.d/routes.conf': ensure => file }` when /etc/app/conf.d is a plain file; a symlink pointing at a file sitting at the parent path; container images or packages shipping a placeholder file where a directory is expected.

Common situations: Name collisions between a file and a needed directory (committed placeholder files); broken symlinks from relocated software; manifests switching a path's role from file to directory without removing the old object.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/d0a13a037c71ab22. Report an issue: GitHub.