hashicorp/nomad · error
unable to parse address template %q: %v
Error message
unable to parse address template %q: %v
What it means
Consul ApiConfig allows the consul address to be a Go text/template that renders to a single IP (e.g. to inject the advertise IP). If listenerutil.ParseSingleIPTemplate fails to render or the result is not a single IP, the config cannot build a consul API client and this error wraps the underlying cause.
Source
Thrown at nomad/structs/config/consul.go:344
tID := *b.TaskIdentity
result.TaskIdentity = &tID
} else if b.TaskIdentity != nil {
result.TaskIdentity = result.TaskIdentity.Merge(b.TaskIdentity)
}
return result
}
// ApiConfig returns a usable Consul config that can be passed directly to
// hashicorp/consul/api. NOTE: datacenter is not set
func (c *ConsulConfig) ApiConfig() (*consul.Config, error) {
// Get the default config from consul to reuse things like the default
// http.Transport.
config := consul.DefaultConfig()
if c.Addr != "" {
ipStr, err := listenerutil.ParseSingleIPTemplate(c.Addr)
if err != nil {
return nil, fmt.Errorf("unable to parse address template %q: %v", c.Addr, err)
}
config.Address = ipStr
}
if c.Token != "" {
config.Token = c.Token
}
if c.Timeout != 0 {
// Create a custom Client to set the timeout
if config.HttpClient == nil {
config.HttpClient = &http.Client{}
}
config.HttpClient.Timeout = c.Timeout
config.HttpClient.Transport = config.Transport
}
if c.Auth != "" {
var username, password string
if strings.Contains(c.Auth, ":") {
split := strings.SplitN(c.Auth, ":", 2)View on GitHub (pinned to 482b49bf1a)
Solutions
- Fix the template syntax in the consul addr to a valid function such as {{ GetPrivateIP }}
- Ensure the template resolves to exactly one IP on the agent
- Use a plain literal address like 127.0.0.1:8500 if no templating is needed
Example fix
// before
addr = "{{PrivateIP}}"
// after
addr = "{{ GetPrivateIP }}" Defensive patterns
Strategy: validation
Validate before calling
addr := cfg.Consul.Addr
if strings.Contains(addr, "{{") {
if _, err := listenerutil.ParseSingleIPTemplate(addr); err != nil {
return fmt.Errorf("consul addr template invalid: %w", err)
}
} else if net.ParseIP(strings.Split(addr, ":")[0]) == nil {
return fmt.Errorf("consul addr %q is not a valid IP", addr)
} Try / catch
cfg, err := consulCfg.ApiConfig()
if err != nil {
return fmt.Errorf("building consul client: %w (check consul.addr template)", err)
} Prevention
- Test Go template expressions like {{ GetPrivateIP }} on the target host first
- Prefer literal IP:port addresses unless templating is required
- Ensure templates resolve to a single IP, not hostnames or ranges
When it happens
Trigger: Calling ApiConfig() with a consul block whose addr contains a template like {{ GetPrivateIP }} that fails to parse/execute, or whose addr is otherwise not a valid single-IP expression.
Common situations: Template syntax typos ({{PrivateIP}} instead of {{ GetPrivateIP }}); template resolving to a hostname or multiple IPs; malformed literal addresses like '127.0.0.1:8500x'.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- no such consul cluster: %s
- Invalid task directory given: %q
- Invalid task environment given
- All templates should have same Once value
- Failed to parse signal %q
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/c44d9749bb7cae82.
Report an issue: GitHub.