tray icon indicating copy to clipboard operation
tray copied to clipboard

qz-tray.js:768 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '0') QZ Tray version: 2.2.4 and Vue js

Open robertoedu opened this issue 11 months ago • 11 comments

I am trying to print PDF files that I receive via URL, but I am encountering an error.

qz-tray.js:768 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '0') at Object.versionCompare (qz-tray.js:768:1) at Object.data (qz-tray.js:863:1) at Object.print (qz-tray.js:1645:1) at reader.onloadend (EtiquetasIntelipostPage.vue:339:1)

Whenever it reaches the print part, this error occurs.

Here is how I connect to the WebSocket and sign the requests:

<q-btn unelevated color="primary" :disable="!selectedDevice" label="Imprimir" class="q-ml-xl flex flex-rigth" @click="getNotafiscal(filtros)" />

const getNotafiscal = (filtros = {}) => { notafiscalService.getIntelipost(filtros).then((response) => { if (response.status === 200) { playSound('success') printLabel(response.data.data.content.label_url) } }).catch(errors => { $q.notify({ color: 'warning', message: GetErrorMessage(errors) }) }) } const printLabel = async (url) => { console.log('URL do PDF:', url)

  try {
    if (!isQZConnected.value) {
      await connectQZTray()
    }

    const response = await fetch(url)
    if (!response.ok) throw new Error('Erro ao baixar etiqueta')

    const blob = await response.blob()
    console.log('Blob do arquivo:', blob)

    const reader = new FileReader()

    reader.onloadend = async () => {
      if (!reader.result) {
        console.error('Erro: O FileReader não conseguiu ler o arquivo.')
        return
      }

      const base64Data = reader.result.split(',')[1]
      console.log('Tamanho do Base64:', base64Data.length)

      if (!base64Data || base64Data.length < 1000) {
        console.error('Erro: Base64 gerado é muito pequeno, pode estar corrompido.')
        return
      }

      if (!selectedDevice.value) {
        console.error('Nenhuma impressora foi selecionada!')
        return
      }

      const version = await qz.api.getVersion()
      console.log('QZ Tray versão:', version)

      // 🔹 Configuração de impressão
      const config = qz.configs.create(selectedDevice.value, {
        margins: 0,
        scaleContent: false,
        orientation: 'portrait'
      })

      // 🔹 Opções testadas
      const data = [{
        type: 'pixel',
        format: 'pdf',
        flavor: 'base64',
        data: base64Data
      }]

      console.log('Enviando para a impressora...')
      console.log('QZ Tray está ativo?', qz.websocket.isActive())
      await qz.print(config, data)
        .then(() => console.log('Etiqueta enviada para a impressora!'))
        .catch(error => console.error('Erro ao imprimir etiqueta:', error))
    }

    reader.readAsDataURL(blob) // Converte para Base64
  } catch (error) {
    console.error('Erro ao imprimir etiqueta:', error)
  }
}

robertoedu avatar Mar 13 '25 05:03 robertoedu

Can you try to use the qz-tray.js version from our master branch and see if it goes away?

tresf avatar Mar 13 '25 05:03 tresf

Just copy and paste the qz-tray-js in node-modules?

robertoedu avatar Mar 13 '25 05:03 robertoedu

Just copy and paste the qz-tray-js in node-modules?

Yes... I think this is a duplicate of #1320 #1301

tresf avatar Mar 13 '25 05:03 tresf

Just copy and paste the qz-tray-js in node-modules?

Yes... I think this is a duplicate of #1320 #1301

Well, that message is gone, but now my setCertificatePromise and setSignaturePromise are not working.

robertoedu avatar Mar 13 '25 06:03 robertoedu

I am fetching the certificate and trying to connect to the WebSocket, but after connecting, when I try to sign it as described in the documentation, the signing process does not occur, and an error is returned.

const connectQZTray = async () => {
      try {
        if (!qz.websocket.isActive()) {
          console.log('Conectando ao QZ Tray...')
          qz.security.setCertificatePromise(() => {
            return fetch(window.location.origin + '/digital-certificate.txt', {
              cache: 'no-store',
              headers: { 'Content-Type': 'text/plain' }
            })
              .then(response => {
                if (!response.ok) {
                  return response.text().then(text => Promise.reject(new Error(`Erro ao carregar certificado: ${text}`)))
                }
                return response.text()
              })
              .then(certText => {
                isCertificateLoaded.value = true
                return certText
              })
              .catch(error => {
                return Promise.reject(error)
              })
          })
          await qz.websocket.connect({ retries: 5, delay: 2 }).then(() => {
            qz.security.setSignatureAlgorithm('SHA512')
            qz.security.setSignaturePromise((toSign) => {
              console.log('Solicitando assinatura para:', toSign)
              return fetch('http://localhost:8080/qzsignature?request=TESTE')
                .then(response => {
                  if (!response.ok) {
                    return response.text().then(text => Promise.reject(new Error(`Erro na assinatura: ${text}`)))
                  }
                  return response.text()
                })
                .then(signature => {
                  console.log('Assinatura recebida:', signature)
                  return signature
                })
                .catch(error => {
                  console.error('Erro ao assinar requisição:', error)
                  throw error
                })
            })
            console.log('Conectado ao QZ Tray.')
            // 5 tentativas com delay de 2s
            console.log('QZ Tray conectado!')
            isQZConnected.value = true // Marca que o QZ Tray está conectado
          })
        }
      } catch (error) {
        console.error('Erro ao conectar ao QZ Tray:', error)
      }
    }
const printLabel = async (url) => {
      console.log('URL do PDF:', url)

      try {
        if (!isQZConnected.value) {
          await connectQZTray()
        }

        const response = await fetch(url)
        if (!response.ok) throw new Error('Erro ao baixar etiqueta')

        const blob = await response.blob()
        console.log('Blob do arquivo:', blob)

        const reader = new FileReader()

        reader.onloadend = async () => {
          if (!reader.result) {
            console.error('Erro: O FileReader não conseguiu ler o arquivo.')
            return
          }

          const base64Data = reader.result.split(',')[1]
          console.log('Tamanho do Base64:', base64Data.length)

          if (!base64Data || base64Data.length < 1000) {
            console.error('Erro: Base64 gerado é muito pequeno, pode estar corrompido.')
            return
          }

          if (!selectedDevice.value) {
            console.error('Nenhuma impressora foi selecionada!')
            return
          }

          const version = await qz.api.getVersion()
          console.log('QZ Tray versão:', version)

          // 🔹 Configuração de impressão
          const config = qz.configs.create(selectedDevice.value, {
            margins: 0,
            scaleContent: false,
            orientation: 'portrait'
          })

          // 🔹 Opções testadas
          const data = [{
            type: 'pixel',
            format: 'pdf',
            flavor: 'base64',
            data: base64Data
          }]

          console.log('Enviando para a impressora...')
          console.log('QZ Tray está ativo?', qz.websocket.isActive())
          await qz.print(config, data)
            .then(() => console.log('Etiqueta enviada para a impressora!'))
            .catch(error => console.error('Erro ao imprimir etiqueta:', error))
        }

        reader.readAsDataURL(blob) // Converte para Base64
      } catch (error) {
        console.error('Erro ao imprimir etiqueta:', error)
      }
    }

qz-tray.js:768 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '0') at Object.versionCompare (qz-tray.js:768:1) at Object.data (qz-tray.js:863:1) at Object.print (qz-tray.js:1646:1) at reader.onloadend (EtiquetasIntelipostPage.vue:338:1)

robertoedu avatar Mar 13 '25 07:03 robertoedu

It is expected that setCertificatePromise and setSignaturePromise are passed a promise-style function (resolve, reject) => however your function never calls resolve() or reject().

This code is untested, but these are the mistakes that I have found...

-          qz.security.setCertificatePromise(() => {
+          qz.security.setCertificatePromise((resolve, reject) => {
            return fetch(window.location.origin + '/digital-certificate.txt', {
              cache: 'no-store',
              headers: { 'Content-Type': 'text/plain' }
            })
              .then(response => {
                if (!response.ok) {
-                 return response.text().then(text => Promise.reject(new Error(`Erro ao carregar certificado`)))
+                 reject(`Erro ao carregar certificado: ${text}`)
                }
+               else {
                return response.text()
+               }
              })
              .then(certText => {
                isCertificateLoaded.value = true
-               return certText
+               resolve(certText)
              })
              .catch(error => {
-               return Promise.reject(error)
+               console.error('Error ...', error)
+               reject(error)
              })
          })
            qz.security.setSignaturePromise((toSign) => {
+            return (resolve, reject) => {
              console.log('Solicitando assinatura para:', toSign)
              return fetch('http://localhost:8080/qzsignature?request=TESTE')
                .then(response => {
                  if (!response.ok) {
                    reject(`Erro na assinatura`)
                  }
+                 else {
                return response.text()
+                 }
                })
                .then(signature => {
                  console.log('Assinatura recebida:', signature)
-                 return signature
+                 resolve(signature)
                })
                .catch(error => {
                  console.error('Erro ao assinar requisição:', error)
-                 throw error
+                 reject(error)
                })
+             }
            })

tresf avatar Mar 13 '25 12:03 tresf

Closing to due lack of response. Please reopen if you believe this was closed in error.

tresf avatar Mar 18 '25 15:03 tresf

Hi @tresf & Team,

We use qz-tray on Prime Web (our app) for different print operations. Our end users also have reported somewhat similar issue where once Prime Web goes into background state, after some time few of the prints start failing due to above same error.

TypeError: Cannot read properties of undefined (reading '0')

Further when we tried debugging above issue on our end, we noticed that it is not reproducible everytime and occurs at random instances, only after Prime Web goes into background state.

We were able to get below console logs screenshot when the error was thrown.

Image

// Code as per stack trace :

Prime Web code which triggers the print ->

function printImageOnDevice(image, jobName, device, noOfCopies = 1, extraConfig = null) {
  const data = [
    {
      type: 'pixel',
      format: 'image',
      flavor: 'base64',
      data: image,
    },
  ];
  const config = createPrintConfig(device, jobName, noOfCopies, extraConfig);
  return getQz(0).then(qz => qz.print(config, data));
}

qz-tray print ->

print: function(configs, data) {
  // ...//...

  //clean up data formatting
  for(var d = 0; d < data.length; d++) {
      _qz.tools.relative(data[d]);
      _qz.compatible.data(data[d]);
  }

  var sendToPrint = function(mapping) {
  // ...//...
}

qz-tray compatible.data ->

compatible: {
/** Converts message format to a previous version's */
data: function(printData) {
    // special handling for Uint8Array
    for(var i = 0; i < printData.length; i++) {
       // ...//...
    }

    if(_qz.tools.versionCompare(2, 2, 4) < 0) {
       // ...//...
    }
}

qz-tray tools.versionCompare ->

versionCompare: function(major, minor, patch, build) {
    if (_qz.tools.assertActive()) {
        var semver = _qz.websocket.connection.semver;
        if (semver[0] != major) {
            return semver[0] - major;
        }
        if (minor != undefined && semver[1] != minor) {
            return semver[1] - minor;
        }
        // ...//...
        return 0;
    }
},

Looking at the stack trace and console logs, our assumption is that due to some reason the qz-tray connection with Prime Web is getting disconnected, which is further leading to above mentioned error.

Kindly let us know if this assumption is incorrect or if there is something else which is going wrong ?

  • Any idea on what could be causing the connection issue ?
  • Is it due to Prime Web going in a background state (browser tab change or browser itself is in background) ?
  • Is there a way to improve the connection between application and qz-tray when in background state ?

ibrahimkundlik avatar Apr 21 '25 10:04 ibrahimkundlik

@ibrahimkundlik since your issue originates from the call to print (specifically to data), it appears there are more race conditions/guards that are needed to fix this problem.

tresf avatar Apr 21 '25 15:04 tresf

Any idea on what could be causing the connection issue ?

It's hard to say. Capturing the disconnect event would be helpful so that you can disable any print functionality until a connection is established. A good place to start is:

  • https://qz.io/api/qz.websocket#.setClosedCallbacks
  • https://qz.io/api/qz.websocket#.setErrorCallbacks

Is it due to Prime Web going in a background state (browser tab change or browser itself is in background) ?

It could be. If so, we may need a dedicated bug report to tack QZ's ability to handle a sleeping tab.

Is there a way to improve the connection between application and qz-tray when in background state ?

At a high-level, checking qz.websocket.isActive()https://qz.io/api/qz.websocket#.isActive before any calls to qz.print may be a good stop-gap. I have a feeling that the error you're receiving is a symptom of a closed connection. We need to do a better job of handling messaging around this.

tresf avatar Apr 21 '25 15:04 tresf

Thanks @tresf.

For now will add above mentioned checks [setClosedCallbacks + isActive] and monitor it for some time to see if it resolves the issue or not.

ibrahimkundlik avatar Apr 23 '25 05:04 ibrahimkundlik

We are experiencing a similar issue. It seems to occur when rapidly sending multiple jobs to qz using qz.print. The issue does not occur when delaying the sending of the jobs

Wouter01 avatar Jul 01 '25 16:07 Wouter01

We are experiencing a similar issue. It seems to occur when rapidly sending multiple jobs to qz using qz.print. The issue does not occur when delaying the sending of the jobs

Does it still occur with [email protected]?

tresf avatar Jul 01 '25 19:07 tresf

We are experiencing a similar issue. It seems to occur when rapidly sending multiple jobs to qz using qz.print. The issue does not occur when delaying the sending of the jobs

Does it still occur with [email protected]?

Forgot to mention that, yes, I updated to 2.2.5 as I read it fixed a related issue, but it still occurs

Wouter01 avatar Jul 01 '25 20:07 Wouter01

@Wouter01 thanks for testing the latest. Assuming it's the same message as the OP reported, we have 25 instances of an unchecked [0]. Much like the bugs that came before this, the most likely candidate is still the compat/versioning check here:

https://github.com/qzind/tray/blob/8a1ba0dcbaabbf098d4c896ab5cd05c706627bf3/js/qz-tray.js#L765-L782

Potential improvements:

  • Guard [0] by checking that it has an element prior to using this value
  • Consider guarding other element access by same or similar technique

I'll put together a quick patch and we can see if it improves this problem.

tresf avatar Jul 02 '25 15:07 tresf

@Wouter01 can you see if the patch in https://github.com/qzind/tray/pull/1350 helps?

tresf avatar Jul 02 '25 15:07 tresf

@Wouter01 can you see if the patch in #1350 helps?

Tried it out, that does seem to resolve the issue! I'm not getting any errors and all the print jobs seem to go through

Wouter01 avatar Jul 02 '25 16:07 Wouter01

@Wouter01 can you see if the patch in #1350 helps?

Tried it out, that does seem to resolve the issue! I'm not getting any errors and all the print jobs seem to go through

Thanks for the testing. It'll be a while before this is available via NPM, but please feel free to use the copy from master branch until then. Clong as resolved.

tresf avatar Jul 03 '25 16:07 tresf