{"record":{"id":"7ad42862dcbb1dd3","repo":"nats-io/nats-server","slug":"encountered-panic-without-a-token-v","errorCode":null,"errorMessage":"encountered panic without a token %v","messagePattern":"encountered panic without a token (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/opts.go","lineNumber":1009,"sourceCode":"\t\t\t*lastToken = tk\n\t\t}\n\t\treturn tk, tk.Value()\n\tdefault:\n\t\treturn nil, v\n\t}\n}\n\n// use in defer to recover from panic and turn it into an error associated with last token\nfunc convertPanicToErrorList(lastToken *token, errors *[]error) {\n\t// only recover if an error can be stored\n\tif errors == nil {\n\t\treturn\n\t} else if err := recover(); err == nil {\n\t\treturn\n\t} else if lastToken != nil && *lastToken != nil {\n\t\t*errors = append(*errors, &configErr{*lastToken, fmt.Sprint(err)})\n\t} else {\n\t\t*errors = append(*errors, fmt.Errorf(\"encountered panic without a token %v\", err))\n\t}\n}\n\n// use in defer to recover from panic and turn it into an error associated with last token\nfunc convertPanicToError(lastToken *token, e *error) {\n\t// only recover if an error can be stored\n\tif e == nil || *e != nil {\n\t\treturn\n\t} else if err := recover(); err == nil {\n\t\treturn\n\t} else if lastToken != nil && *lastToken != nil {\n\t\t*e = &configErr{*lastToken, fmt.Sprint(err)}\n\t} else {\n\t\t*e = fmt.Errorf(\"%v\", err)\n\t}\n}\n\n// configureSystemAccount configures a system account","sourceCodeStart":991,"sourceCodeEnd":1027,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/opts.go#L991-L1027","documentation":"This error is produced by a deferred panic-recovery helper in server/opts.go when a panic occurs during configuration processing but no configuration token was recorded to attach the panic to. Because the library cannot attribute the panic to a specific config option, it emits this generic error into the collected errors list. It exists so config parsing panics become normal errors instead of crashing the server.","triggerScenarios":"Any panic raised inside a parse* function (parseCluster, parseGateway, etc.) invoked through the recover helper whose lastToken is nil — i.e., the panic fired before a config block token was set.","commonSituations":"Malformed or unexpected config value types causing type-assertion panics very early in option parsing; a parser bug triggered before token assignment.","solutions":["Inspect the panic value %v in the message to find which parse path panicked.","Validate your config file values (correct types for ports, URLs, arrays) before starting the server.","Binary-search the config file by removing sections until the panic disappears, then fix that section.","If caused by a library bug on a valid config, report it with the redacted config."],"exampleFix":"// before (config triggering a pre-token panic)\ncluster: 12345\n// after\ncluster {\n\tport: 12345\n}","handlingStrategy":"validation","validationCode":"// sanity-check config values' types before starting the server\nfor key, val := range cfg {\n\tswitch val.(type) {\n\tcase map[string]any, []any, string, int64, bool, nil:\n\tdefault:\n\t\tfmt.Printf(\"unexpected type %T for key %s\\n\", val, key)\n\t}\n}","typeGuard":"func isScalarOrBlock(v any) bool {\n\tswitch v.(type) {\n\tcase map[string]any, []any, string, int64, float64, bool, nil:\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":null,"preventionTips":["Validate the config file structure before deployment.","Keep option blocks in documented map/list shapes.","Test config changes in a staging server first."],"tags":["config","panic","recovery"],"backgroundTag":"panic-during-config-parse","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}