Troubleshooting

Error: “can’t modify frozen…”

Once the compilation process has started, content, and attributes of layouts and items are frozen, which means they cannot be modified anymore. For example, the following rule is invalid and will cause a “can’t modify frozen Array” error:

compile '/articles/**/*'
  item[:tags] << 'article'
  filter :erb
  layout 'default'
end

What is possible, is modifying content and attributes in the preprocess phase. The preprocess phase is defined using the preprocess block in the Rules file (see the Rules page). For example:

preprocess do
  items.select { |i| i.identifier =~ '/articles/**/*' }.each do |i|
    i[:tags] << 'article'
  end
end

Character encoding issues

Character encoding issues manifest themselves in two ways:

  • Some characters do not show up correctly in the output (e.g., ä shows up as ä).

  • Nanoc exits with an error such as RegexpError: invalid multibyte character.

In both cases, text in one character encoding is erroneously interpreted as a different character encoding. There are two possible causes for this.

Wrong output encoding tag

The text could be in the correct encoding, but Nanoc or the browser interpret it wrongly.

Nanoc’s output is always UTF-8, so the output files should not declare a different encoding. For example, having <meta charset="iso-8859-1"> at the top of files in output/ is wrong: it should be <meta charset="utf-8"> instead. You should also ensure that your web server sends the right Content-Type.

Wrong input encoding

The data sources could interpret the input data in the wrong encoding.

Nanoc defaults to the current environment encoding, which might not be what you expect. If the environment encoding does not match the actual file encoding, it can lead to errors in the output. There are three ways to solve this:

  • You can re-encode your site’s files. If your content files are not in UTF-8, this is probably a good start. Re-encoding into something else than UTF-8 is not recommended.

  • You can modify your environment encoding to match the file encoding. If you run into encoding issues with other sites or libraries, it isn’t a bad idea to set your environment up as UTF-8 and get it over with. You should not change your environment to a non-UTF-8 encoding, as UTF-8 is considered the standard character encoding.

  • You can set an explicit encoding in the Nanoc configuration file. This is the recommended approach, as it never hurts to be explicit.

To set the encoding explicitly in the site configuration, open nanoc.yaml (or config.yaml on older Nanoc sites) and navigate to the section where the data sources are defined. Unless you have modified this section, you will find a single entry for the filesystem data source there. In this section, add something similar to encoding: utf-8 (replacing utf-8 with whatever you really want). It could look like this:

data_sources:
  -
    type: filesystem
    encoding: utf-8

For bonus points, you can do all three. Setting up your content, environment, and configuration as UTF-8 is the best way to avoid encoding issues now and in the future.

Timestamps in YAML files parsed incorrectly

If you work with date/time attributes (such as created_at, published_at, and updated_at) and find that the time is one or more hours off, then this section applies to you.

If you use timestamps in the YAML file, be sure to include the timezone. If no timezone is specified, then UTC is assumed—not the local time zone! Quoting the YAML timestamp specification:

If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part may be omitted altogether, resulting in a date format. In such a case, the time part is assumed to be 00:00:00Z (start of day, UTC).

We recommend always specifying the time zone.

Pages are not being recompiled

Nanoc tracks data and detects changes, and knows which items to recompile and which ones not to recompile. However, it can only track data that it knows about, and it cannot track changes to global state.

The following kinds of data are not tracked by Nanoc:

  • Global variables (e.g. <%= $foo %>)
  • Constants (e.g. <%= RUBY_VERSION %>)
  • The environment (e.g. <%= ENV["PROD"] %>)
  • The filesystem (e.g. <%= File.read('oink.txt') %>)
  • Time-related data (e.g. <%= Time.now %>)
  • Random data (e.g. <%= rand(1..10) %>)
  • Dynamically evaluated data (e.g. <%= `ruby --version`.strip %>)
  • … and possibly more

It is not feasible for Nanoc (or any Ruby program) to track global state.

Global state creates other problems with Nanoc, too. For example, it is possible to get NoMethodError errors when using global variables; see the Nanoc objects are stored in global variables section for an idea.

To work around this problem, use the preprocessor to populate the configuration, e.g.

preprocess do
  config[:is_staging] = ENV['STAGING'] == 'true'
end

Then, only refer to the configuration rather than global state, e.g.

<% if @config[:is_staging] %>
  <span class="label">STAGING</span>
<% end %>

Changes to global state (ENV in the example above) will now be detected, and the appropriate pages will be recompiled properly.

Hidden files are ignored

Nanoc by default intentionally ignores files and directories that have a name that starts with a period. Such files and directories are hidden on Unix-like systems, such as macOS and Linux. Nanoc does so because these hidden files and directories are often generated unintentionally; for instance,macOS will generate .DS_Store files, and vim generates backup files with a name like .about.md.un~.

The filesystem data source can be told to include specific files that would otherwise be ignored. To do so, specify in the data source’s extra_files attribute the patterns for the files that you would like to be included. For example, the following will include GitHub’s .nojekyll file and all files in the .well-known/ directory:

data_sources:
  -
    type: filesystem
    extra_files:
      - "/.well-known/**/*"
      - "/.nojekyll"

Alternatively, route a file whose name does not start with a slash to a path that starts with a slash. For example, the following will copy content/nojekyll to output/.nojekyll:

compile '/nojekyll' do
  write '/.nojekyll'
end

Error: AmbiguousMetadataAssociation

This error occurs when there are multiple content files with the same basename, along with a file containing metadata, and Nanoc cannot unambiguously figure out to which content file the metadata file belongs.

For example, assume the following files:

  • content/about.md
  • content/about.txt
  • content/about.yaml
<!-- This file: content/about.md -->
Stuff about me goes here…
<!-- This file: content/about.txt -->
Stuff about my dog goes here…
# This file: content/about.yaml
title: About me

The content/about.yaml contains metadata, and could be associated with either content file (content/about.md or content/about.txt). Nanoc has no way of knowing which file to associate the metadata with, and errors.

There is one exception in which case Nanoc will not error: if exactly one of the content files has no inline metadata (also known as frontmatter), Nanoc will associate the metadata file to this particular content file. For example, in the following case the metadata will be associated with about.txt, because it is the only file without inline metadata:

<!-- This file: content/about.md -->
Stuff about me goes here…
<!-- This file: content/about.txt -->
---
title: About my dog
---

Stuff about my dog goes here…
# This file: content/about.yaml
title: About me

To fix the AmbiguousMetadataAssociation error, the recommended approach is to give each content file a distinct name (e.g. content/about_myself.md and content/about_my_dog.md).

Error: CannotDetermineFilter

This error occurs when the compilation rule for an item refers to a layout for which no rule exists. For example, a rules file that contains nothing but the following will raise this error:

compile '/index.html' do
  layout '/default.*'
  write '/index.html'
end

To fix this error, ensure that a rule exists for the layout. The example above can be fixed as follows (assuming that the default layout has the filename layouts/default.erb):

compile '/index.html' do
  layout '/default.*'
  write '/index.html'
end

layout '/*.erb', :erb

Error: CannotLayoutBinaryItem

Nanoc only supports laying out textual items, and will raise the CannotLayoutBinaryItem error when attempting to lay out binary items.

If the error occurs for an item that you expected to be textual (rather than binary), verify that its extension is included in the text_extensions array in the site configuration. Refer to the text_extensions configuration reference for details.

Error: CannotUseBinaryFilter

Refer to the CannotUseBinaryFilter section.

Error: CannotUseTextualFilter

Nanoc deliberately prevents textual filters from being applied to binary items, and will raise the CannotUseTextualFilter error in that case.

To fix this error:

  • If the error occurs for an item that you expected to be textual (rather than binary), verify that its extension is included in the text_extensions array in the site configuration. Refer to the text_extensions configuration reference for details.

  • If the error occurs for a filter that you expect to handle binary items, verify that its type is binary. Filters are text-to-text by default. Refer to the Binary filters section on the Filters page for details.

  • If the error occurs in a filter that you did not expect to be called, check the Rules file and verify that the correct rule is being applied to the item. Nanoc will use the first matching rule only. The show-rules command is helpful in showing which rules are being applied.

Error: DependencyCycle

This error occurs when two or more items depend on each other’s compiled content. For example, imagine these two files:

<!-- content/foo.html -->
<%= @items['/bar.*'].compiled_content %>
<!-- content/bar.html -->
<%= @items['/foo.*'].compiled_content %>

The compiled content of the foo.html item depends on the compiled content of the bar.html item, and vice versa. Nanoc cannot resolve this, and throws a DependencyCycle error.

To fix this error, ensure that at least one of these items does not depend on the compiled content of the other.

This error also occurs when a single item depends on the later compiled content of itself. For example, an item with the following content will also cause a DependencyCycle error:

<%= @item.compiled_content %>

Error: InvalidEncoding

This error occurs when the file content cannot be interpreted in the encoding that Nanoc has inferred. For example, the following piece of code will generate a file that will cause an InvalidEncoding error if Nanoc assumes the file to be encoded as UTF-8:

File.write('content/broken.md', '¿', encoding: 'ISO-8859-1')

See the Character encoding issues section for details on how to fix this issue.

Error: FilterReturnedNil

This error occurs when a textual filter returns nil instead of a string. For example, the following filter will cause such an error:

Nanoc::Filter.define(:nillify) do |content, params|
  nil
end

To fix this error, fix the filter’s code to always return a string.

Error: InvalidFormat

This error occurs when Nanoc detects the presence of frontmatter, but the frontmatter isn’t terminated properly. For example, the following file will cause an InvalidFormat error, because the frontmatter is missing a final ---:

---
title: Home

Here is some content.

To fix this error, terminate the frontmatter properly. For example:

---
title: Home
---

Here is some content.

This error can also occur when the file is not supposed to have frontmatter, and the initial --- is meant to be part of the file content. For example, the following will also raise an InvalidFormat error:

---

This file starts with three dashes!

To tell Nanoc to treat these three dashes part of the content, rather than signaling the start of the frontmatter, add an artificial empty frontmatter:

---
---

---

This file starts with three dashes!

Error: InvalidFullIdentifierError

This error occurs when trying to construct a full identifier for an item or layout with a trailing slash. For instance:

Nanoc::Identifier.new('/hello/')

This issue will not occur with legacy identifier types:

Nanoc::Identifier.new('/hello/', type: :legacy)

To fix this problem, ensure that full identifiers don’t end with a slash:

Nanoc::Identifier.new('/hello')

Error: InvalidIdentifierError

This error occurs when trying to construct an identifier for an item or layout without a leading slash. For instance:

Nanoc::Identifier.new('hello')

To fix this problem, ensure that all identifiers start with a slash:

Nanoc::Identifier.new('/hello')

Error: InvalidMetadata

This error occurs when the frontmatter is not a collection of key-value pairs. For example, the following file will cause an UnparseableMetadata error:

---
My about page
---

Hello! This page has some details about myself.

To fix this error, ensure the YAML in the front matter is a collection of key-value pairs. For example:

---
title: My about page
---

Hello! This page has some details about myself.

This error can also occur when the file is not supposed to have frontmatter, and the initial --- is meant to be part of the file content. For example, the following will also raise an UnparseableMetadata error:

---

Once upon a time, …

---

Another section…

To tell Nanoc to treat these three dashes part of the content, rather than signaling the start of the frontmatter, add an artificial empty frontmatter:

---
---

---

Once upon a time, …

---

Another section…

Error: MultipleContentFiles

This error occurs for legacy identifiers when there are multiple files with the same base name, but different extensions. When using legacy identifiers, Nanoc requires each base name to be unique. For example, the following situation will give raise to this error:

content/assets/fonts/foo.eot
content/assets/fonts/foo.otf

Nanoc converts these filenames to identifiers which do not contain the file extension. In the example given above, both filenames correspond to the identifier /assets/fonts/foo/. Identifiers are required to be unique, and thus Nanoc raises an error.

Nanoc 4 supports full identifiers, which, unlike legacy identifiers, contain the file extension (e.g. /about.md instead of /about/). We recommend enabling full identifiers, as well as glob patterns. For details, see the Nanoc 4 upgrade guide page and the Identifiers and patterns page.

Error: NoMethodError

A NoMethodError occurs when attempting to call a method that does not exist — at least not on the object that you’re calling the method on. This can have several causes:

  • A method is misspelled
  • A helper is not activated
  • Your code stores Nanoc objects in global variables

The following sections describe these problems in detail.

A method is misspelled

The method might not be spelled correctly. For example:

"hello".uppercase
NoMethodError (undefined method `uppercase' for "hello":String)

To resolve this error, verify that the method is correctly spelled. In the example above, uppercase should have been upcase instead:

"hello".upcase
HELLO

To browse and search Ruby’s API, refer to the Ruby documentation on APIdock or the Ruby documentation on DevDocs.

A helper is not activated

When a helper method raises a NoMethodError, the helper might not yet be activated. For example, the following will raise a NoMethodError if the helper is not activated yet:

<%= link_to 'About', @items['/about.*'] %>

To fix this problem, activate the helper by calling #use_helper, with the helper as the argument, somewhere in lib/. For example, to activate the LinkTo helper:

# in lib/helpers.rb
use_helper Nanoc::Helpers::LinkTo
<%# the following works now! %>
<%= link_to 'About', @items['/about.*'] %>

Nanoc objects are stored in global variables

If Nanoc objects are passed around through global variables, Nanoc will behave in odd ways, such as raising NoMethodError exceptions in unusual situations.

For example, the following will raise an error:

preprocess do
  $articles = @items.find_all('/articles/*')
end

compile '/index.*' do
  filter :list_articles, articles: $articles.map(&:path)
  layout '/default.*'
  write ext: 'html'
end

This NoMethodError happens because objects in the preprocess block has a different type than in the compilation rule. During preprocessing, items do not have paths, but in a compilation rule, items do have a path. However, passing items around using a global variable means that they will be of the wrong type in the compilation rule.

The solution: do not use global variables. The fact that Nanoc breaks when using global variables is not a bug in Nanoc; global variables generally cause many problems, and Nanoc will not be changed to support them.

Avoid global variables outside of Nanoc, too. They make code brittle. The internet has plenty of resources about the drawbacks of global variables, such as the Global Variables Are Bad page on WikiWikiWeb.

To fix this NoMethodError, remove the use of global variables:

compile '/index.*' do
  articles = @items.find_all('/articles/*')
  filter :list_articles, articles: articles.map(&:path)
  layout '/default.*'
  write ext: 'html'
end

Error: OutputNotWritten

This error occurs when a binary filter does not create a file at the path defined by output_filename. For example, the following filter will cause such an error:

Class.new(Nanoc::Filter)
  identifier :sample
  type :binary

  def run(content, params = {})
    # … do nothing!
  end
end

To fix this error, fix the filter’s code to always create a file at output_filename:

Class.new(Nanoc::Filter)
  identifier :sample
  type :binary

  def run(content, params = {})
    File.write(output_filename, 'contents go here')
  end
end

Error: UnknownDataSource

This error occurs when the nanoc.yaml configuration file lists a data source that is not known to Nanoc. For example, the following will cause an UnknownDataSource if the file_system data source is not defined:

data_sources:
  -
    type: file_system

To fix this error, verify that the data source name is spelled correctly. For example, the example above needs filesystem rather than file_system:

data_sources:
  -
    type: filesystem

This errors also occurs if the source files that define the data source have not yet been loaded. For example, if a (hypothetical) nanoc-aether gem defines a data source named aether, then that gem must be required before the data source can be used. In a file in the lib/ directory (e.g. lib/default.rb), add a require for the gem:

require 'nanoc-aether'

Error: UnknownFilter

This error occurs when a compilation rule calls a filter that Nanoc does not know about. For example, the following will cause an UnknownFilter error, assuming that there is no filter named :kram_down:

compile '/articles/*.md' do
  filter :kram_down
  layout '/article.*'
  write item.identifier.without_ext + '/index.html'
end

To fix this error, verify the spelling of the filter name in the rules file. The example above errors because the filter name is misspelled as kram_down; fixing the spelling to kramdown makes the error disappear:

compile '/articles/*.md' do
  filter :kramdown
  layout '/article.*'
  write item.identifier.without_ext + '/index.html'
end

This errors also occurs if the source files that define the filter have not yet been loaded. For example, if a (hypothetical) nanoc-transmogrify gem defines a filter named transmogrify, then that gem must be required before the filter can be used. In a file in the lib/ directory (e.g. lib/default.rb), add a require for the gem:

require 'nanoc-transmogrify'

Error: UnknownLayout

This error occurs when the compilation rule for an item specifies a layout that does not exist. For example, the rule below will cause an UnknownLayout error, assuming that there is no layout with an identifier matching the /artikle.* pattern:

compile '/articles/*.md' do
  filter :kramdown
  layout '/artikle.*'
  write item.identifier.without_ext + '/index.html'
end

To fix this error, verify that the layout exists and has the correct identifier, and verify that the correct identifier is used in the rules. In the example above, the identifier of the layout is misspelled as /artikle.*, and fixing the spelling by changing it to /article.* makes the error disappear:

compile '/articles/*.md' do
  filter :kramdown
  layout '/article.*'
  write item.identifier.without_ext + '/index.html'
end

Error: UnparseableMetadata

This error occurs when the frontmatter is not valid YAML. For example, the following file will cause an UnparseableMetadata error:

---
[Not valid YAML!
---

Here is some content.

To fix this error, ensure the YAML in the front matter is valid. For example:

---
title: Home
---

Here is some content.

For more information about YAML, see the YAML for Ruby guide, as well as the YAML specification.

This error can also occur when the file is not supposed to have frontmatter, and the initial --- is meant to be part of the file content. For example, the following will also raise an UnparseableMetadata error:

---

[0, 1[ is the interval that contains all real numbers between zero and one, including zero, but not including one.

---

Another section…

To tell Nanoc to treat these three dashes part of the content, rather than signaling the start of the frontmatter, add an artificial empty frontmatter:

---
---

---

[0, 1[ is the interval that contains all real numbers between zero and one, including zero, but not including one.

---

Another section…