puppetlabs/puppet · error · Puppet::Error
#{detail} on node #{node_name}
Error message
#{detail} on node #{node_name} What it means
This is Puppet::Parser::ScriptCompiler#compile's catch-all rescue, not a distinct failure itself. When compiling a script (the ScriptCompiler path used for one-shot script execution with an environment), any exception that is NOT a Puppet::ParseErrorWithIssue gets re-wrapped as Puppet::Error with " on node <node_name>" appended and logged via Puppet.log_exception. The original exception is preserved as the cause with its backtrace, so the real fault is always the wrapped `detail`.
Source
Thrown at lib/puppet/parser/script_compiler.rb:60
# TRANSLATORS, "For running script" is not user facing
Puppet.override(@context_overrides, "For running script") do
# TRANSLATORS "main" is a function name and should not be translated
result = Puppet::Util::Profiler.profile(_("Script: Evaluated main"), [:script, :evaluate_main]) { evaluate_main }
if block_given?
yield self
else
result
end
end
rescue Puppet::ParseErrorWithIssue => detail
detail.node = node_name
Puppet.log_exception(detail)
raise
rescue => detail
message = "#{detail} on node #{node_name}"
Puppet.log_exception(detail, message)
raise Puppet::Error, message, detail.backtrace
end
# Constructs the overrides for the context
def context_overrides
{
:current_environment => environment,
:global_scope => @topscope, # 4x placeholder for new global scope
:loaders => @loaders, # 4x loaders
:rich_data => true,
}
end
# Create a script compiler for the given environment where errors are logged as coming
# from the given node_name
#
def initialize(environment, node_name, for_agent = false)
@environment = environment
@node_name = node_nameView on GitHub (pinned to e227c27540)
Solutions
- Read the full exception chain: the wrapped original is available as the Puppet::Error's cause (or via `--trace` / Puppet.log_exception output) — fix that underlying error, not the wrapper.
- Run with `--trace` (or Puppet[:trace] = true) to get the original backtrace showing which function/expression raised.
- If the failure is intermittent (network, Hiera backend), make the failing call conditional/retried at the script level before re-running compile.
- In Ruby embedding (pal), rescue Puppet::Error and inspect `error.cause` to branch on the real failure class.
Example fix
# before (embedding pal/script compiler)
begin
compiler.compile
rescue Puppet::Error => e
puts e.message # "RuntimeError ... on node web1" — real cause hidden
end
# after
begin
compiler.compile
rescue Puppet::Error => e
root = e.cause || e
puts "#{root.class}: #{root.message}"
puts root.backtrace
end Defensive patterns
Strategy: try-catch
Validate before calling
# Validate the script cheaply before compiling: Puppet::Pops::Parser::EvaluatingParser.new.validate_string(script_code) # (catches parse-level issues; runtime failures still need rescue below)
Try / catch
begin script_compiler.compile rescue Puppet::ParseErrorWithIssue => e # already has file/line — handle directly report(e) rescue Puppet::Error => e root = e.cause || e # unwrap the "on node X" wrapper report(root) end
Prevention
- Always inspect error.cause (or run with --trace) — the node-suffixed message is only a wrapper.
- Validate/parse scripts before compiling them in pipelines.
- Keep custom functions defensive so they raise Puppet::Error subclasses with file/line instead of raw Ruby errors.
- Pin environments and module versions so loader-level Ruby exceptions do not surface at compile time.
When it happens
Trigger: ScriptCompiler#compile (or the Puppet::Pal / `puppet apply`-style script entry points that use it) evaluating a script whose code raises: a failing function call, a Ruby error inside a custom function, an assert_type failure, a failed lookup — anything outside ParseErrorWithIssue. The rescue fires per-compile, tags the node name, and re-raises Puppet::Error wrapping the original.
Common situations: Running manifests/scripts through pal/script compilation where a custom function raises a RuntimeError; environment or loader problems surfacing as plain Ruby exceptions; users confused because the visible message is the generic '<original message> on node mynode' and the actionable detail is nested inside.
Related errors
- %{message} on node %{node}
- Somehow looped more than 1000 times while evaluating host ca
- PathPatterns cannot be created with a zero byte.
- Request to Puppet Forge failed. Detail: %{detail}.
- Unable to verify the SSL certificate at %{uri}
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/2c0a6f5a41d8a0cd.
Report an issue: GitHub.