{"record":{"id":"8df2321917bcd424","repo":"nats-io/nats-server","slug":"config-is-invalid-s-d-d","errorCode":null,"errorMessage":"config is invalid (%s:%d:%d)","messagePattern":"config is invalid \\((.+?):(.+?):(.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"conf/parse.go","lineNumber":212,"sourceCode":"\tp := newParser(data, \"\", false)\n\tp.envVarReferences = parent.envVarReferences\n\tif err := p.parse(\"\"); err != nil {\n\t\treturn nil, err\n\t}\n\treturn p, nil\n}\n\nfunc (p *parser) parse(fp string) error {\n\tp.pushContext(p.mapping)\n\n\tvar prevItem item\n\tfor {\n\t\tit := p.next()\n\t\tif it.typ == itemEOF {\n\t\t\t// Here we allow the final character to be a bracket '}'\n\t\t\t// in order to support JSON like configurations.\n\t\t\tif prevItem.typ == itemKey && prevItem.val != mapEndString {\n\t\t\t\treturn fmt.Errorf(\"config is invalid (%s:%d:%d)\", fp, it.line, it.pos)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tprevItem = it\n\t\tif err := p.processItem(it, fp); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (p *parser) next() item {\n\treturn p.lx.nextItem()\n}\n\nfunc (p *parser) pushContext(ctx any) {\n\tp.ctxs = append(p.ctxs, ctx)\n\tp.ctx = ctx","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/conf/parse.go#L194-L230","documentation":"parse() walks lexer items and rejects configs that end while a key was pushed but never assigned a value (and the last token wasn't the closing '}'). It reports \"config is invalid\" with file:line:pos of EOF. This catches truncated or JSON-like configs that stop after a key without a value.","triggerScenarios":"A config file ends right after a key with no value or closing brace, e.g. file truncated mid-edit or a JSON-style config missing a value before EOF; parse is called recursively (includes/env) via parse/parseEnv.","commonSituations":"Cut-and-paste losing the last lines of nats.conf; an editor dropping a trailing value; hand-written JSON-like config ending in a dangling key such as `{ \"port\": 4222, \"http\" }`.","solutions":["Open the file:line:pos from the error and supply a value for the dangling key","Restore the truncated tail of the config (check version control or backup)","For JSON-style configs, ensure valid JSON: every key has a value and braces balance","Run `nats-server -t -c file.conf` to syntax-check before deploying"],"exampleFix":"// before\nlisten: 4222\nhttp\n// after\nlisten: 4222\nhttp: 8222","handlingStrategy":"validation","validationCode":"// quick sanity check before parsing: braces balanced and non-empty\nfunc configLooksComplete(data []byte) error {\n    depth := 0\n    for _, r := range string(data) {\n        if r == '{' { depth++ }\n        if r == '}' { depth-- }\n    }\n    if depth != 0 {\n        return errors.New(\"unbalanced braces: config likely truncated\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"cfg, err := conf.ParseFile(fp)\nif err != nil {\n    if strings.Contains(err.Error(), \"config is invalid (\") {\n        log.Fatalf(\"config %s ends with a dangling key — check the tail of the file\", fp)\n    }\n    return err\n}","preventionTips":["Always end block-style configs with a closing brace","Lint configs with nats-server -t in CI before deploy","Avoid hand-editing the last lines of generated configs","Keep configs in version control to restore truncated files"],"tags":["config","syntax","parser","nats-server"],"backgroundTag":"config-syntax-error","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}