Issue search API - Cannot read property 'match' of undefined

I’ve encountered a weird behavior of the /rest/api/2/search in my Forge application.

Here is an example code:

import Resolver from '@forge/resolver'
import { asUser } from '@forge/api'

const resolver = new Resolver()

resolver.define('loadMarkersForIssues', async (request) => {
  const { payload, context: { extensionContext } } = request

  const queryParams = [
    `jql=${encodeURIComponent(payload.jql)}`
  ]
  console.log(queryParams)
  const searchResponse = await asUser().requestJira(`/rest/api/2/search?${queryParams.join('&')}`)
  if (searchResponse.ok) {
    const json = await searchResponse.json()
    // do something with the data
    return new Promise(resolve => {
      resolve({data: '...'})
    })
  } else {
    return new Promise((resolve, reject) => {
      reject("bad query string...")
    })
  }
})

Here is sample output for two executions: valid JQL and some invalid characters:

invocation: d2e568d0926c6ca7 map-project.handler
INFO    14:53:47.287  d2e568d0926c6ca7  [ 'jql=assignee%20%3D%20currentUser()' ]

invocation: 524d407da3e45988 map-project.handler
INFO    14:54:24.153  524d407da3e45988  [ 'jql=asdf%20asdf%20asdf%20asdf%20asdf' ]
ERROR   14:54:24.331  524d407da3e45988  TypeError: Cannot read property 'match' of undefined
    at Object.transformScriptError [as default] (/tunnel/node_modules/@forge/runtime/out/sandbox/transform-script-error.js:14:21)
    at /tunnel/node_modules/@forge/runtime/out/sandbox-isolate/index.js:151:57
    at async IsolateSandbox.execute (/tunnel/node_modules/@forge/runtime/out/sandbox-isolate/index.js:41:23)
    at async LocalInvocationService.invoke (/tunnel/node_modules/@forge/tunnel/out/tunnelling/services/local-invocation-service.js:46:26)
    at async LocalDevelopmentServer.handleInvocation (/tunnel/node_modules/@forge/tunnel/out/tunnelling/servers/dev-server.js:23:30)

As you can see, I am not touching searchResponse.json() if searchResponse.ok is false (see reference here: Problems with Rest API Commenting with attachments - #11 by kchan)

package.json:

  "dependencies": {
    "@forge/api": "^1.1.0",
    "@forge/resolver": "^1.1.2"
  },

@forge/cli updated to the latest version 1.3.2

1 Like

Hi @kbujacek!

It looks as though we have a bug when handling the rejected Promise in the else branch of your code. This is causing the error, which should look something like:

ERROR   03:34:08.420  351f0cf830cf3e41  bad query string
Error: bad query string

to instead crash with the Cannot read property 'match' of undefined you’re seeing instead.

We are actively looking into fixing this issue as we speak :slightly_smiling_face:. For now, you can throw an error in the else branch instead of returning a rejected promise, like so:

if (searchResponse.ok) {
  const json = await searchResponse.json()
  // do something with the data
  return new Promise(resolve => {
    resolve();
  })
} else {
  throw new Error('bad query string');
}
1 Like

Dear JackShe,

Is the ‘bad query string’ the only one case when searchResponse.ok might be false? I feel a little bit uncomfortable when the else branch is narrowed to only one fail case :slight_smile:

The “bad query string” I’m referring to was just in reference to the error message you used to reject the Promise in your original code snippet :slight_smile:.

There are likely more potential errors that can arise when requesting the Confluence Rest API, which you can access via searchResponse.statusText after you’ve detected that searchResponse.ok is false.

Dear @JackShe,

I am not sure whether your proposed fix is good one in my case.

Initially I thought that returning a promise allows me to easily distinguish between successful and fail state. However, in reality, returning plain object like { searchResponse.ok, payload } is less error prone and easier to handle. I should check for the boolean value of sR.ok , but now I am not seeing error messages in the logs.

Probably returning a promise from a @forge/resolver definitions is not a good idea, because it is (or it may be?) processed by any method inside of @forge/resolver or @forge/bridge package. Am I correct in this?