It seems that you did not follow the instructions. It was the argument to OPENJSON you should change, not the argument to ISJON. Thus change:
SELECT rf.[FieldId], rf.[Name], rf.[Description], rf.[FieldType], rf.[AdditionalData],
val.[ValueSmallText], val.[ValueLongText], val.[ValueNumber], val.[ValueDate], val.ValueBool, val. [ValuePerson], persVal.[json]
FROM dbo.[InstanceField] rf
INNER JOIN dbo.[InstanceFieldValue] val ON rf.[FieldId] = val.[FieldId]
OUTER APPLY(
SELECT u.[Id] AS UserId, u.[Login], u.[Name], u.[Email],
t.[Id] AS TeamId, t.[IntId], t.[Title], t.[TypeId]
FROM (OPENJSON(val.[ValueLongText], '$.Users')
WITH ( Id uniqueidentifier '$.Id' ) userVal
FULL OUTER JOIN OPENJSON(val.[ValueLongText], '$.Teams')
WITH ( Id uniqueidentifier '$.Id' ) teamVal ON 1=2 --dumb but necessary
)
LEFT JOIN dbo.[User] u ON userVal.[Id] = u.[Id]
LEFT JOIN dbo.[Team] t ON teamVal.[Id] = t.[Id]
WHERE ISJSON(try_cast(val.[ValueLongText] as json)) > 0
FOR JSON PATH
) AS persVal(json)
WHERE rf.[InstanceId] = @instanceId
to
SELECT rf.[FieldId], rf.[Name], rf.[Description], rf.[FieldType], rf.[AdditionalData],
val.[ValueSmallText], val.[ValueLongText], val.[ValueNumber], val.[ValueDate], val.ValueBool, val. [ValuePerson], persVal.[json]
FROM dbo.[InstanceField] rf
INNER JOIN dbo.[InstanceFieldValue] val ON rf.[FieldId] = val.[FieldId]
OUTER APPLY(
SELECT u.[Id] AS UserId, u.[Login], u.[Name], u.[Email],
t.[Id] AS TeamId, t.[IntId], t.[Title], t.[TypeId]
FROM (OPENJSON(try_cast(val.[ValueLongText] as json), '$.Users')
WITH ( Id uniqueidentifier '$.Id' ) userVal
FULL OUTER JOIN OPENJSON(try_cast(val.[ValueLongText] as json), '$.Teams')
WITH ( Id uniqueidentifier '$.Id' ) teamVal ON 1=2 --dumb but necessary
)
LEFT JOIN dbo.[User] u ON userVal.[Id] = u.[Id]
LEFT JOIN dbo.[Team] t ON teamVal.[Id] = t.[Id]
WHERE ISJSON(val.[ValueLongText]) > 0
FOR JSON PATH
) AS persVal(json)
WHERE rf.[InstanceId] = @instanceId
Casting the argument to ISJSON does not help. The problem, as I explained, is that the call to OPENJSON could be evaluated before the call to ISJSON, because the silly optimizer thinks that this is more efficient. To make sure that the query does not die, you need to ensure that the argument that OPENJSON sees either valid JSON or a NULL value. The TRY_CAST achieves that, since it returns NULL if the conversion fails.
I used TRY_CAST because that was the first thing that came to my mind, but you could also use
OPENJSON(CASE WHEN ISJSON(val.ValueLongText) = 1 THEN val.ValueLongText ELSE NULL END)
The important thing is that the filter in the WHERE clause is not sufficient.
(This is quite a classic problem, just a new twist with JSON. A common pattern is something like this:
SELECT ..., cast(stringcol as int) * 2
FROM tbl
WHERE isnumeric(stringcol) = 1
People are blown away then when the query fails with a conversion error, when it logically should not. The remedy is to change cast to try_cast.)