Repository: promptfoo/promptfoo
Issue: Web Viewer error: `e.score.toFixed` is not a function
Root cause: Validation Gap


![CleanShot 2024-03-12 at 00 11 40](https://github.com/promptfoo/promptfoo/assets/10948652/51cf3efc-8224-42df-826b-460fd74e26ff)


![CleanShot 2024-03-12 at 00 02 17](https://github.com/promptfoo/promptfoo/assets/10948652/1cf23e36-5557-4db5-bf4a-37de244d1fab)


Hi @kevinxh, I can patch this problem but I'm curious about the root cause.  Which assert types are running on this eval output?

Hi @typpo ! Thanks for replying!

I have a custom javascript assert function.

```js
{
            type: 'javascript',
            value: async (output, testCase) => {
              return {
                pass: false,
                score: 50,
                reason: 'test'
              }
            }
          }
```

The `javascript` assertion doesn't take an inline async function like that.  It should look something like this: https://promptfoo.dev/docs/configuration/expected-outputs/javascript/#multiline-functions

Or you can use an async function in an external file.

That said, I am going to leave this issue open because the eval should fail in a more understandable way if the custom javascript is malformed.  Let me know if you have any more qs!

Sorry @typpo , I forgot to clarify, i'm using the Nodejs package.

```js
const results = await promptfoo.evaluate({
    providers: (prompt: string, context: CallApiContextParams | undefined) => {
      const provider = new Provider()
      return provider.callApi(prompt, context)
    },
    prompts: ['{{input}}'],
    tests: [
      {
        // description will be used as screenshot image filename
        // do not use spaces in description
        description: 'tdx-marketing-banner-two-columns',
        vars: {
          input: `input`
        },
        assert: [
          {
            type: 'javascript',
            value: async (output, testCase) => {
       
              const reason = await getScore()
              if (!reason) {
                return {
                  pass: false,
                  score: 0,
                  reason: 'UNKNOWN. something went wrong and cannot get reason.'
                }
              }

              return {
                pass: false,
                score: score ? score : 0,
                reason
              }
            }
          }
        ]
      }
    ],
    writeLatestResults: true
  })
```

This is my simplified code (deleted some lines that doesn't matter to this issue)

first of all, your typescript SDK types does indicate the assert function value can be an async function. And it makes total sense, because i need it to be asyc because i'm using a custom model to grade my output and that's an async call.



The async function also works and i have tested it. so it is not the issue here.

The issue lies in the implementation of the web viewer client side

Oh, you're right. I was thinking you were putting that in a yaml config for some reason.  In that case yes, this is probably a bug that needs fixing.

You can probably reproduce this issue by using this:

```js
const results = await promptfoo.evaluate({
    providers: ['openai:gpt-3.5-turbo'],
    prompts: ['{{input}}'],
    tests: [
      {
        // description will be used as screenshot image filename
        // do not use spaces in description
        description: 'tdx-marketing-banner-two-columns',
        vars: {
          input: `input`
        },
        assert: [
          {
            type: 'javascript',
            value: (output, testCase) => {
              return {
                pass: false,
                score: 0,
                reason
              }
            }
          }
        ]
      }
    ],
    writeLatestResults: true
  })
```

Again, thank you for looking into this and make this package great!

It doesn't seem to repro with that example because `score` is always set.  The webui is crashing because `score` is not a `number`.  Is it possible there is something wrong with the result that your custom logic is returning?  I still think the best fix here is to detect malformed return values at eval time rather than catch bad values on the frontend after they are saved in the eval.

#545 will catch invalid score values (and other invalid properties on `GradingResult`)

Yea, that's a good solution. 

It would be even better if the validation prints out why value is bad, something like "Validation failed: Score is not a number" ...

Will do!

Published in 0.47.0