Repository: caddyserver/caddy
File: caddy.go (Go)

Code before:
		return err
	}

	// the mutation is complete, so encode the entire config as JSON
	newCfg, err := json.Marshal(rawCfg[rawConfigKey])
	if err != nil {
		return APIError{
			HTTPStatus: http.StatusBadRequest,
			Err:        fmt.Errorf("encoding new config: %v", err),
		}
	}

	// if nothing changed, no need to do a whole reload unless the client forces it
	if !forceReload && bytes.Equal(rawCfgJSON, newCfg) {
		Log().Info("config is unchanged")
		return errSameConfig
	}

	// find any IDs in this config and index them
	idx := make(map[string]string)
	err = indexConfigObjects(rawCfg[rawConfigKey], "/"+rawConfigKey, idx)
	if err != nil {
		err2 := rollbackRawCfg()
		if err2 != nil {
			err = errors.Join(err, err2)
		}
		return APIError{
			HTTPStatus: http.StatusInternalServerError,
			Err:        fmt.Errorf("indexing config: %v", err),
		}
	}

	// load this new config; if it fails, we need to revert to
	// our old representation of caddy's actual config
	err = unsyncedDecodeAndRun(newCfg, true)
	if err != nil {
		if len(rawCfgJSON) > 0 {
			// restore old config state to keep it consistent
			// with what caddy is still running; we need to
			// unmarshal it again because it's likely that
			// pointers deep in our rawCfg map were modified
			err2 := rollbackRawCfg()
			if err2 != nil {
				err = fmt.Errorf("%v; additionally, restoring old config: %v", err, err2)
			}
		}

		return fmt.Errorf("loading new config: %v", err)
	}

	// success, so update our stored copy of the encoded

Review comment (refactor):
Trying to parse the old config here without checking the length of `rawCfgJSON` isn't a recommended approach. Additionally, this is a potential duplicate of the call in `Ln246`

Code after:

	err := unsyncedConfigAccess(method, path, input, nil)
	if err != nil {
		return err
	}

	// the mutation is complete, so encode the entire config as JSON
	newCfg, err := json.Marshal(rawCfg[rawConfigKey])
	if err != nil {
		return APIError{
			HTTPStatus: http.StatusBadRequest,
			Err:        fmt.Errorf("encoding new config: %v", err),
		}
	}

	// if nothing changed, no need to do a whole reload unless the client forces it
	if !forceReload && bytes.Equal(rawCfgJSON, newCfg) {
		Log().Info("config is unchanged")
		return errSameConfig
	}

	// find any IDs in this config and index them
	idx := make(map[string]string)
	err = indexConfigObjects(rawCfg[rawConfigKey], "/"+rawConfigKey, idx)
	if err != nil {
		if len(rawCfgJSON) > 0 {
			var oldCfg any
			err2 := json.Unmarshal(rawCfgJSON, &oldCfg)
			if err2 != nil {
				err = fmt.Errorf("%v; additionally, restoring old config: %v", err, err2)
			}
			rawCfg[rawConfigKey] = oldCfg
		} else {
			rawCfg[rawConfigKey] = nil
		}
		return APIError{
			HTTPStatus: http.StatusBadRequest,
			Err:        fmt.Errorf("indexing config: %v", err),
		}
	}

	// load this new config; if it fails, we need to revert to
	// our old representation of caddy's actual config
	err = unsyncedDecodeAndRun(newCfg, true)
	if err != nil {
		if len(rawCfgJSON) > 0 {
			// restore old config state to keep it consistent
			// with what caddy is still running; we need to
			// unmarshal it again because it's likely that
			// pointers deep in our rawCfg map were modified
			var oldCfg any