gotify/server · warning

cannot render config: %w

Error message

cannot render config: %w

What it means

Once the old config is decoded, Config builds the env var map (buildEnv) and renders it as .env content with godotenv.Marshal. If Marshal fails, the error is wrapped as 'cannot render config: %w'. This is rare because Marshal mostly fails on values that cannot be represented as dotenv entries.

Source

Thrown at config/migrate/migrate.go:90

}

func Config(file string) (string, error) {
	if file == "" {
		return "", errors.New("migrate-config requires one argument: the path to the old config.yml")
	}
	data, err := os.ReadFile(file)
	if err != nil {
		return "", fmt.Errorf("cannot read config file %s: %w", file, err)
	}

	var migrated oldConfig
	if err := yaml.Unmarshal(data, &migrated); err != nil {
		return "", fmt.Errorf("cannot parse config file %s: %w", file, err)
	}

	content, err := godotenv.Marshal(buildEnv(migrated))
	if err != nil {
		return "", fmt.Errorf("cannot render config: %w", err)
	}

	return content, nil
}

func buildEnv(c oldConfig) map[string]string {
	out := map[string]string{}
	str := func(key string, value *string) {
		if value != nil {
			out[key] = *value
		}
	}
	num := func(key string, value *int) {
		if value != nil {
			out[key] = strconv.Itoa(*value)
		}
	}
	boolean := func(key string, value *bool) {

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Inspect the values buildEnv produces (log the map) and quote/escape problematic values (newlines, quotes, backslashes) before Marshal.
  2. Upgrade or pin the godotenv dependency to a version whose Marshal handles your values.
  3. Sanitize values in buildEnv (e.g. strconv.Quote strings with control characters).
  4. As a fallback, render the map yourself with fmt.Printf("%s=%q\n", k, v) instead of godotenv.Marshal.

Example fix

// before
content, err := godotenv.Marshal(env) // fails on multiline secret
// after
for k, v := range env { env[k] = strings.ReplaceAll(v, "\n", "\\n") }
content, err := godotenv.Marshal(env)
Defensive patterns

Strategy: try-catch

Validate before calling

for k, v := range envMap {
    if strings.ContainsAny(v, "\n\r") {
        return fmt.Errorf("value for %s contains a newline and may fail dotenv rendering", k)
    }
}

Try / catch

content, err := migrate.Config(oldPath)
if err != nil && strings.Contains(err.Error(), "cannot render config") {
    // dump intermediate map to inspect offending values
    return fmt.Errorf("rendering failed, check values for special chars: %w", err)
}

Prevention

When it happens

Trigger: godotenv.Marshal returns an error while serializing the map produced by buildEnv — typically when a migrated value contains characters that cannot be represented in a dotenv file without quoting issues in the installed godotenv version.

Common situations: Old config values containing newlines or special characters that break dotenv rendering, or a godotenv library version with stricter marshaling.

Related errors


AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05). Data as JSON: /api/errors/03f4d5fa086fa463. Report an issue: GitHub.