Skip to content

Commit ebb5f67

Browse files
authored
UX: better error message for json errors while parsing options (#4602)
This predominantly tries to actually list the problematic part of the options.
1 parent 604cd5e commit ebb5f67

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

internal/js/bundle.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ func (b *Bundle) populateExports(updateOptions bool, bi *BundleInstance) error {
217217
dec.DisallowUnknownFields()
218218
if err = dec.Decode(&b.Options); err != nil {
219219
if uerr := json.Unmarshal(data, &b.Options); uerr != nil {
220+
uerr = beautifyJSONUnmarshalError(data, uerr)
220221
err = errext.WithAbortReasonIfNone(
221222
errext.WithExitCodeIfNone(uerr, exitcodes.InvalidConfig),
222223
errext.AbortedByScriptError,
@@ -247,6 +248,19 @@ func (b *Bundle) populateExports(updateOptions bool, bi *BundleInstance) error {
247248
return nil
248249
}
249250

251+
func beautifyJSONUnmarshalError(data []byte, err error) error {
252+
unmarshalTypError := new(json.UnmarshalTypeError)
253+
if errors.As(err, &unmarshalTypError) {
254+
e := unmarshalTypError
255+
previousNewLineIndex := max(bytes.LastIndexByte(data[:e.Offset], '\n'), 0)
256+
nextNewLineIndex := max(bytes.IndexByte(data[e.Offset:], '\n'), len(data)-1)
257+
258+
info := strings.TrimSpace(string(data[previousNewLineIndex:nextNewLineIndex]))
259+
err = fmt.Errorf("parsing options from script got error while parsing %q: %w", info, e)
260+
}
261+
return err
262+
}
263+
250264
// Instantiate creates a new runtime from this bundle.
251265
func (b *Bundle) Instantiate(ctx context.Context, vuID uint64) (*BundleInstance, error) {
252266
// Instantiate the bundle into a new VM using a bound init context. This uses a context with a

internal/js/bundle_test.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,24 @@ func TestNewBundle(t *testing.T) {
230230
invalidOptions := map[string]struct {
231231
Expr, Error string
232232
}{
233-
"Array": {`[]`, "json: cannot unmarshal array into Go value of type lib.Options"},
234-
"Function": {`function(){}`, "error parsing script options: json: unsupported type: func(sobek.FunctionCall) sobek.Value"},
233+
"Array": {
234+
`[]`,
235+
`parsing options from script got error while parsing "[": ` +
236+
`json: cannot unmarshal array into Go value of type lib.Options`,
237+
},
238+
"Bad value": {
239+
`{"tags":["something"]}`,
240+
`parsing options from script got error while parsing "{\"tags\":[\"something\"]": ` +
241+
`json: cannot unmarshal array into Go struct field Options.tags of type map[string]string`,
242+
},
243+
"Function": {
244+
`function(){}`,
245+
"error parsing script options: json: unsupported type: func(sobek.FunctionCall) sobek.Value",
246+
},
235247
}
236248
for name, data := range invalidOptions {
237249
t.Run(name, func(t *testing.T) {
250+
t.Parallel()
238251
_, err := getSimpleBundle(t, "/script.js", fmt.Sprintf(`
239252
export let options = %s;
240253
export default function() {};

0 commit comments

Comments
 (0)