nats-io/nats-server · error
%v
Error message
%v
What it means
convertPanicToError recovers from a panic during configuration processing and converts it to an error: if a config token is known it becomes a configErr tied to that option, otherwise it becomes fmt.Errorf("%v", err) — this bare message. Developers see the raw panic value (e.g. a runtime type-assertion message) with no wrapper text, attributed to no specific option when no token exists.
Source
Thrown at server/opts.go:1023
return
} else if lastToken != nil && *lastToken != nil {
*errors = append(*errors, &configErr{*lastToken, fmt.Sprint(err)})
} else {
*errors = append(*errors, fmt.Errorf("encountered panic without a token %v", err))
}
}
// use in defer to recover from panic and turn it into an error associated with last token
func convertPanicToError(lastToken *token, e *error) {
// only recover if an error can be stored
if e == nil || *e != nil {
return
} else if err := recover(); err == nil {
return
} else if lastToken != nil && *lastToken != nil {
*e = &configErr{*lastToken, fmt.Sprint(err)}
} else {
*e = fmt.Errorf("%v", err)
}
}
// configureSystemAccount configures a system account
// if present in the configuration.
func configureSystemAccount(o *Options, m map[string]any) (retErr error) {
var lt token
defer convertPanicToError(<, &retErr)
configure := func(v any) error {
tk, v := unwrapValue(v, <)
sa, ok := v.(string)
if !ok {
return &configErr{tk, "system account name must be a string"}
}
o.SystemAccount = sa
return nil
}
View on GitHub (pinned to 3a66a489d2)
Solutions
- Read the panic text (e.g. 'interface conversion: interface {} is string, not map[string]interface {}') and fix the value type at that config key.
- Ensure each option block uses the documented structure (maps for blocks, arrays for routes/clusters).
- Run the server with a minimal config and add sections back until the panic reappears.
- Check the config against the current version's sample config — option shapes change between releases.
Example fix
// before routes: nats-route://host:4222 // after routes: [ nats-route://host:4222 ]
Defensive patterns
Strategy: validation
Validate before calling
// ensure block options are maps before running the server
for _, block := range []string{"cluster", "gateway", "leafnodes", "monitor"} {
if v, ok := cfg[block]; ok {
if _, isMap := v.(map[string]any); !isMap && v != nil {
fmt.Printf("%s must be a block, got %T\n", block, v)
}
}
} Type guard
func isBlock(v any) bool {
_, ok := v.(map[string]any)
return ok
} Prevention
- Quote values that must remain strings in YAML.
- Compare configs against the sample config for the running version.
- Diff config changes and review scalar-vs-block mistakes.
When it happens
Trigger: A panic inside any config parsing helper deferred with convertPanicToError where lastToken is nil, e.g. a failed type assertion like v.(map[string]any) on a scalar config value.
Common situations: YAML/JSON config where a block (host:port map, routes list) is given as a scalar or wrong type; empty or null sections; wrong nesting levels in the config file.
Related errors
- encountered panic without a token %v
- expected port or host:port, got %T
- must be int64 or string
- ErrBadSigningAlgorithm
- mqtt authentication token not compatible with presence of us
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/aa131cfeb1fb584b.
Report an issue: GitHub.