@@ -11,6 +11,7 @@ import (
1111 "os"
1212 "path/filepath"
1313 "slices"
14+ "strconv"
1415 "strings"
1516
1617 "github.com/goccy/go-yaml"
@@ -47,7 +48,11 @@ func Load(ctx context.Context, source Source) (*latest.Config, error) {
4748
4849 oldConfig , err := parseCurrentVersion (data , raw .Version )
4950 if err != nil {
50- return nil , fmt .Errorf ("parsing config file\n %s" , yaml .FormatError (err , true , true ))
51+ msg := yaml .FormatError (err , true , true )
52+ if hint := newerVersionHint (data , raw .Version , err ); hint != "" {
53+ msg += "\n " + hint
54+ }
55+ return nil , fmt .Errorf ("parsing config file\n %s" , msg )
5156 }
5257
5358 config , err := migrateToLatestConfig (oldConfig , data )
@@ -172,6 +177,42 @@ func parseCurrentVersion(data []byte, version string) (any, error) {
172177 return parser (data )
173178}
174179
180+ // newerVersionHint returns a user-facing hint when a strict-parse failure is
181+ // caused by a key that a newer schema version accepts. It tries the parsers
182+ // for every version above the declared one, in order, and points the user at
183+ // the smallest version that parses the config successfully. Best-effort: a
184+ // newer version may accept the config for unrelated reasons (laxer schema),
185+ // so the original unknown-field error is always shown before the hint.
186+ func newerVersionHint (data []byte , version string , parseErr error ) string {
187+ var unknownField * yaml.UnknownFieldError
188+ if ! errors .As (parseErr , & unknownField ) {
189+ return ""
190+ }
191+
192+ current , err := strconv .Atoi (version )
193+ if err != nil {
194+ return ""
195+ }
196+
197+ parsers , _ := versions ()
198+ var newer []int
199+ for v := range parsers {
200+ if n , err := strconv .Atoi (v ); err == nil && n > current {
201+ newer = append (newer , n )
202+ }
203+ }
204+ slices .Sort (newer )
205+
206+ for _ , n := range newer {
207+ v := strconv .Itoa (n )
208+ if _ , err := parsers [v ](data ); err == nil {
209+ return fmt .Sprintf ("hint: this key is supported by config version %s; update the top-level 'version' field (currently %s)" , v , version )
210+ }
211+ }
212+
213+ return ""
214+ }
215+
175216func migrateToLatestConfig (c any , raw []byte ) (latest.Config , error ) {
176217 var err error
177218
0 commit comments