Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error with fs.exists wrapper. #287

Open
EthanShoeDev opened this issue Jan 19, 2023 · 4 comments
Open

Error with fs.exists wrapper. #287

EthanShoeDev opened this issue Jan 19, 2023 · 4 comments

Comments

@EthanShoeDev
Copy link

I am trying to use AppOptics in a nodejs project that uses the prisma library. I am also using esm imports, so I am instantiating AppOptics like so:

node -r appoptics-apm dist/src/main

The sequence of events that leads to the error:

  1. function patchPathMethod (fs, method) within this library wraps the fs.exists() method.
  2. Prisma begins its initialization and checks what OS it is running on. That results in this method being called ref:
export async function resolveDistro(): Promise<undefined | GetOSResult['distro']> {
  // https://github.com/retrohacker/getos/blob/master/os.json
  const osReleaseFile = '/etc/os-release'
  const alpineReleaseFile = '/etc/alpine-release'

  if (await exists(alpineReleaseFile)) {
    return 'musl'
  } else if (await exists(osReleaseFile)) {
    return parseDistro(await readFile(osReleaseFile, 'utf-8'))
  } else {
    return
  }
}

exists() is simply a promisified version of fs.exists()

const exists = promisify(fs.exists)
  1. The second call to exsits() causes the boolean value true to be thrown instead of returned as it should be. The thrown boolean cascades into this error which crashes the application:
TypeError: Cannot create property 'clientVersion' on boolean 'true'
    at RequestHandler.handleRequestError (/home/project/node_modules/@prisma/client/runtime/index.js:31959:26)
    at RequestHandler.handleAndLogRequestError (/home/project/node_modules/@prisma/client/runtime/index.js:31913:12)
    at /home/project/node_modules/@prisma/client/runtime/index.js:32458:25
    at async PrismaService._executeRequest (/home/project/node_modules/@prisma/client/runtime/index.js:33022:22)
    at async PrismaService._request (/home/project/node_modules/@prisma/client/runtime/index.js:32994:16)

After debugging it for a while, I believe that the problem stems from the fact that patchPathMethod within node_modules/appoptics-apm/lib/probes/fs.js is not meant to override suppressedCallback within the fs library.
I believe the fs library has changed in different versions of node but mine looks like this (node version v16.17.0):

/**
 * Tests whether or not the given path exists.
 * @param {string | Buffer | URL} path
 * @param {(exists?: boolean) => any} callback
 * @returns {void}
 */
function exists(path, callback) {
  maybeCallback(callback);

  function suppressedCallback(err) {
    callback(err ? false : true);
  }

  try {
    fs.access(path, F_OK, suppressedCallback);
  } catch {
    return callback(false);
  }
}
@EthanShoeDev EthanShoeDev changed the title Error with fs.exits wrapper. Error with fs.exists wrapper. Jan 19, 2023
@EthanShoeDev
Copy link
Author

Here are the logs as requested by support:
err.log

@EthanShoeDev
Copy link
Author

EthanShoeDev commented Jan 20, 2023

An extra note.
I do not even need fs monitoring at all. I tried to disable it with the config file but that didn't seem to do anything:
appoptics-apm-config.json

{
    "enabled": true,
    "serviceKey": "REDACTED",
    "probes": {
        "fs": {
            "enabled": false,
            "ignoreErrors": {
                "open": {
                    "ENOENT": true
                },
                "access": {
                    "ENOENT": true
                }
            }
        }
    }
}

@ronilan
Copy link
Contributor

ronilan commented Jan 20, 2023

Try fs config:

registered: false

https://github.com/appoptics/appoptics-apm-node/blob/master/lib/probe-defaults.js#L28

@EthanShoeDev
Copy link
Author

That worked! Thank you!

By the way, here is a minimal reproduction of the error case:

import * as express from 'express';
import { promisify } from 'util';
import * as fs from 'fs';

const exists = promisify(fs.exists);

async function resolveDistro() {
  const osReleaseFile = '/etc/os-release';
  const alpineReleaseFile = '/etc/alpine-release';

  if (await exists(alpineReleaseFile)) {
    return 'musl';
  } else if (await exists(osReleaseFile)) {
    return 'not-musl';
  } else {
    return;
  }
}

const initServer = async () => {
  await resolveDistro();
  const app = express();

  app.get('/', (req, res) => {
    res.send('Successful response.');
  });

  app.listen(3000, () => console.log('Example app is listening on port 3000.'));
};

initServer().catch((err) =>
  console.log('This value was just thrown as an error: ' + err),
);

Results in:

ethan@Ethan-XPS:~/project$ node dist/src/main2
Example app is listening on port 3000.
^C
ethan@Ethan-XPS:~/project$ node -r appoptics-apm dist/src/main2
This value was just thrown as an error: true
^C

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants