{"record":{"id":"bef48a16f20df183","repo":"nektos/act","slug":"cannot-convert-value-to-json-cause-v","errorCode":null,"errorMessage":"Cannot convert value to JSON. Cause: %v","messagePattern":"Cannot convert value to JSON\\. Cause: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/exprparser/functions.go","lineNumber":162,"sourceCode":"\t\tvar items []string\n\t\tfor i := 0; i < array.Len(); i++ {\n\t\t\titems = append(items, impl.coerceToString(array.Index(i).Elem()).String())\n\t\t}\n\n\t\treturn strings.Join(items, separator), nil\n\tdefault:\n\t\treturn strings.Join([]string{impl.coerceToString(array).String()}, separator), nil\n\t}\n}\n\nfunc (impl *interperterImpl) toJSON(value reflect.Value) (string, error) {\n\tif value.Kind() == reflect.Invalid {\n\t\treturn \"null\", nil\n\t}\n\n\tjson, err := json.MarshalIndent(value.Interface(), \"\", \"  \")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"Cannot convert value to JSON. Cause: %v\", err)\n\t}\n\n\treturn string(json), nil\n}\n\nfunc (impl *interperterImpl) fromJSON(value reflect.Value) (interface{}, error) {\n\tif value.Kind() != reflect.String {\n\t\treturn nil, fmt.Errorf(\"Cannot parse non-string type %v as JSON\", value.Kind())\n\t}\n\n\tvar data interface{}\n\n\terr := json.Unmarshal([]byte(value.String()), &data)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Invalid JSON: %v\", err)\n\t}\n\n\treturn data, nil","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/nektos/act/blob/4f411281417e88660bea1c1a1749aa71ae0bd60f/pkg/exprparser/functions.go#L144-L180","documentation":"Thrown by the expression toJSON() function when json.MarshalIndent fails on the value. Common causes: the value contains a channel, func, complex number, or a cyclic data structure that Go's json package cannot serialize.","triggerScenarios":"toJSON(someContextField) where the mapped context value, after act's env conversion, ends up as an unmarshalable Go type; rarely, deeply nested structures hitting encoder limits.","commonSituations":"Calling toJSON() on unusual contexts; values derived from fromJSON round-trips; custom Go code embedding non-serializable fields into the expression environment.","solutions":["Check the %v cause in the message — 'unsupported type' names the offending type.","toJSON a narrower sub-object (e.g. toJSON(inputs.x) instead of toJSON(inputs)).","In custom Go usage, pre-convert funcs/channels or use json.RawMessage.","Verify the value is plain data (maps/slices/strings/numbers/bools)."],"exampleFix":"# before\n${{ toJSON(github.event) }}\n# after (narrow to the needed part)\n${{ toJSON(github.event.commits[0].author) }}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isJSONSerializable(v interface{}) bool {\n  switch reflect.TypeOf(v).Kind() {\n  case reflect.Chan, reflect.Func, reflect.UnsafePointer, reflect.Complex64, reflect.Complex128:\n    return false\n  }\n  _, err := json.Marshal(v)\n  return err == nil\n}","tryCatchPattern":"if s, err := toJSON(v); err != nil {\n  if strings.Contains(err.Error(), 'Cannot convert value to JSON') {\n    s = fmt.Sprintf('%v', v) // degrade to plain rendering\n  } else { return err }\n}","preventionTips":["Keep context values to plain data types","toJSON narrow sub-objects rather than whole contexts","In Go, replace funcs/channels before injecting env"],"tags":["expressions","to-json","serialization","workflow-yaml"],"backgroundTag":null,"analyzedSha":"4f411281417e88660bea1c1a1749aa71ae0bd60f","analyzedAt":"2026-08-15T09:19:46.307Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}