{"record":{"id":"bb15e8735c6f69a6","repo":"semaphoreui/semaphore","slug":"could-not-decode-configuration","errorCode":null,"errorMessage":"Could not decode configuration!","messagePattern":"Could not decode configuration!","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"util/config.go","lineNumber":1894,"sourceCode":"func exitOnConfigError(msg string) {\n\tfmt.Println(msg)\n\tos.Exit(1)\n}\n\nfunc exitOnConfigFileError(err error) {\n\tif err != nil {\n\t\texitOnConfigError(\"Cannot Find configuration! Use --config parameter to point to a JSON or YAML file generated by `semaphore setup`.\")\n\t}\n}\n\nfunc decodeConfig(file io.Reader, configPath string) {\n\tif isYAMLConfig(configPath) {\n\t\tdecodeConfigYAML(file)\n\t\treturn\n\t}\n\tif err := json.NewDecoder(file).Decode(&Config); err != nil {\n\t\tfmt.Println(\"Could not decode configuration!\")\n\t\tpanic(err)\n\t}\n}\n\nfunc isYAMLConfig(configPath string) bool {\n\text := strings.ToLower(filepath.Ext(configPath))\n\treturn ext == \".yaml\" || ext == \".yml\"\n}\n\nfunc decodeConfigYAML(file io.Reader) {\n\tvar raw any\n\tif err := yaml.NewDecoder(file).Decode(&raw); err != nil {\n\t\tfmt.Println(\"Could not decode configuration!\")\n\t\tpanic(err)\n\t}\n\tdata, err := json.Marshal(raw)\n\tif err != nil {\n\t\tfmt.Println(\"Could not decode configuration!\")\n\t\tpanic(err)","sourceCodeStart":1876,"sourceCodeEnd":1912,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/util/config.go#L1876-L1912","documentation":"The JSON config file decoder could not parse config.json into the global Config struct. Semaphore prints 'Could not decode configuration!' and panics with the underlying json error. This means the configuration file is syntactically invalid JSON or has incompatible types for known fields.","triggerScenarios":"decodeConfigFile opens a non-YAML config path and json.NewDecoder(file).Decode(&Config) fails: trailing commas, comments, single quotes, wrong types (e.g. string where object expected), or truncated file.","commonSituations":"Hand-edited config.json with a stray comma or comment; config written as YAML but named config.json (or missing .yml extension so the YAML branch is skipped); partially-written file from a failed deploy.","solutions":["Validate the file with `jq . /path/to/config.json` (or any JSON linter) and fix syntax errors","If the file is actually YAML, rename it to config.yml or add the .yaml/.yml extension so the YAML decoder is used","Check field types against the ConfigType struct (objects, not strings, for mysql/postgres blocks)","Restore a known-good config.json from backup or `semaphore setup` output"],"exampleFix":"// before (config.json with trailing comma)\n{\"mysql\": {\"host\": \"db\",},}\n// after\n{\"mysql\": {\"host\": \"db\"}}","handlingStrategy":"validation","validationCode":"// validate JSON config before handing it to semaphore\njq -e . /etc/semaphore/config.json > /dev/null || { echo 'invalid JSON'; exit 1; }","typeGuard":null,"tryCatchPattern":"// wrap decodeConfigFile in recover to convert panic to error\ndefer func() {\n    if r := recover(); r != nil {\n        err = fmt.Errorf(\"config decode failed: %v\", r)\n    }\n}()","preventionTips":["Always `jq . config.json` after hand edits","Never put comments or trailing commas in config.json","Name YAML configs with .yml/.yaml so the right decoder is used","Keep config.json in version control to diff against a known-good copy"],"tags":["go","config","json","panic","startup"],"backgroundTag":"json-decode-failed","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}