Fixing your NPM Installation

Fixing an npm installation gone wrong when 'sudo' is misused.

Fixing your NPM Installation

    There's a bit of a headache going around in respect to global npm permissions. For those of us working on Unix-based systems, it's not uncommon to come across error messages complaining about your user permissions whenever installing new npm packages, especially when installing them globally. If errors like the one below  look familiar, you're not alone:

    npm WARN checkPermissions Missing write access to /Users/toddbirchard/.npm-packages/lib/node_modules
    npm ERR! path /Users/toddbirchard/.npm-packages/lib/node_modules
    npm ERR! code EACCES
    npm ERR! errno -13
    npm ERR! syscall access
    npm ERR! Error: EACCES: permission denied, access '/Users/toddbirchard/.npm-packages/lib/node_modules'
    npm ERR!  { [Error: EACCES: permission denied, access '/Users/toddbirchard/.npm-packages/lib/node_modules']
    npm ERR!   stack:
    npm ERR!    'Error: EACCES: permission denied, access \'/Users/toddbirchard/.npm-packages/lib/node_modules\'',
    npm ERR!   errno: -13,
    npm ERR!   code: 'EACCES',
    npm ERR!   syscall: 'access',
    npm ERR!   path: '/Users/toddbirchard/.npm-packages/lib/node_modules' }
    npm ERR!
    npm ERR! The operation was rejected by your operating system.
    npm ERR! It is likely you do not have the permissions to access this file as the current user
    npm ERR!
    npm ERR! If you believe this might be a permissions issue, please double-check the
    npm ERR! permissions of the file and its containing directories, or try running
    npm ERR! the command again as root/Administrator (though this is not recommended).
    
    npm ERR! A complete log of this run can be found in:
    npm ERR!     /Users/toddbirchard/.npm/_logs/2019-05-05T20_36_45_642Z-debug.log
    

    This is a textbook case of being too trigger-happy with the sudo command. When installing npm as a user other than root, it's a bad idea to install Node + npm with the sudo command. For one, the user that does this will inherently have no permissions to modify global npm packages when this happens; because we installed Node with sudo, the directory for global npm packages forever functions as though the root user has permissions, but nobody else. That means we have to specify sudo before every global install, which is annoying.

    It gets worse. Npm packages which require post-install scripts to be run simply become impossible to install. Sometimes you might be able to get away with the following, but only if the scripts aren't necessary:

    npm i -g [package-name] --ignore-scripts
    

    When the scripts are necessary, like in the case of node-sass, we're shit outta luck. Using the --ignore-scripts flag just results in a broken install. Attempting to install using sudo looks like this:

    $ sudo npm i -g node-sass
    
    [sudo] password for todd:
    /home/todd/.npm-global/bin/node-sass -> /home/todd/.npm-global/lib/node_modules/node-sass/bin/node-sass
    
    > [email protected] install /home/todd/.npm-global/lib/node_modules/node-sass
    > node scripts/install.js
    
    Unable to save binary /home/todd/.npm-global/lib/node_modules/node-sass/vendor/linux-x64-64 : { Error: EACCES: permission denied, mkdir '/home/todd/.npm-global/lib/node_modules/node-sass/vendor'
        at Object.mkdirSync (fs.js:753:3)
        at sync (/home/todd/.npm-global/lib/node_modules/node-sass/node_modules/mkdirp/index.js:71:13)
        at Function.sync (/home/todd/.npm-global/lib/node_modules/node-sass/node_modules/mkdirp/index.js:77:24)
        at checkAndDownloadBinary (/home/todd/.npm-global/lib/node_modules/node-sass/scripts/install.js:114:11)
        at Object.<anonymous> (/home/todd/.npm-global/lib/node_modules/node-sass/scripts/install.js:157:1)
        at Module._compile (internal/modules/cjs/loader.js:689:30)
        at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
        at Module.load (internal/modules/cjs/loader.js:599:32)
        at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
        at Function.Module._load (internal/modules/cjs/loader.js:530:3)
      errno: -13,
      syscall: 'mkdir',
      code: 'EACCES',
      path: '/home/todd/.npm-global/lib/node_modules/node-sass/vendor' }
    
    > [email protected] postinstall /home/todd/.npm-global/lib/node_modules/node-sass
    > node scripts/build.js
    
    Building: /usr/bin/node /home/todd/.npm-global/lib/node_modules/node-sass/node_modules/node-gyp/bin/node-gyp.js rebuild --verbose --libsass_ext= --libsass_cflags= --libsass_ldflags= --libsass_library=
    gyp info it worked if it ends with ok
    gyp verb cli [ '/usr/bin/node',
    gyp verb cli   '/home/todd/.npm-global/lib/node_modules/node-sass/node_modules/node-gyp/bin/node-gyp.js',
    gyp verb cli   'rebuild',
    gyp verb cli   '--verbose',
    gyp verb cli   '--libsass_ext=',
    gyp verb cli   '--libsass_cflags=',
    gyp verb cli   '--libsass_ldflags=',
    gyp verb cli   '--libsass_library=' ]
    gyp info using [email protected]
    gyp info using [email protected] | linux | x64
    gyp verb command rebuild []
    gyp verb command clean []
    gyp verb clean removing "build" directory
    gyp verb command configure []
    gyp verb check python checking for Python executable "python2" in the PATH
    gyp verb `which` succeeded python2 /usr/bin/python2
    gyp verb check python version `/usr/bin/python2 -c "import sys; print "2.7.15
    gyp verb check python version .%s.%s" % sys.version_info[:3];"` returned: %j
    gyp verb get node dir no --target version specified, falling back to host node version: 10.15.2
    gyp verb command install [ '10.15.2' ]
    gyp verb install input version string "10.15.2"
    gyp verb install installing version: 10.15.2
    gyp verb install --ensure was passed, so won't reinstall if already installed
    gyp verb install version is already installed, need to check "installVersion"
    gyp verb got "installVersion" 9
    gyp verb needs "installVersion" 9
    gyp verb install version is good
    gyp verb get node dir target node version installed: 10.15.2
    gyp verb build dir attempting to create "build" dir: /home/todd/.npm-global/lib/node_modules/node-sass/build
    gyp ERR! configure error
    gyp ERR! stack Error: EACCES: permission denied, mkdir '/home/todd/.npm-global/lib/node_modules/node-sass/build'
    gyp ERR! System Linux 4.15.0-43-generic
    gyp ERR! command "/usr/bin/node" "/home/todd/.npm-global/lib/node_modules/node-sass/node_modules/node-gyp/bin/node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags=" "--libsass_library="
    gyp ERR! cwd /home/todd/.npm-global/lib/node_modules/node-sass
    gyp ERR! node -v v10.15.2
    gyp ERR! node-gyp -v v3.8.0
    gyp ERR! not ok
    Build failed with error code: 1
    npm ERR! code ELIFECYCLE
    npm ERR! errno 1
    npm ERR! [email protected] postinstall: `node scripts/build.js`
    npm ERR! Exit status 1
    npm ERR!
    npm ERR! Failed at the [email protected] postinstall script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
    
    npm ERR! A complete log of this run can be found in:
    npm ERR!     /home/todd/.npm/_logs/2019-05-05T20_52_06_518Z-debug.log
    

    Why does this happen? Because by default, the post-install scripts attempt to run as the current user. Since the current user doesn't have permissions to mess with packages installed globally, the installation ultimately fails. To fix this, we need to reinstall Node the correct way.

    Reinstalling Node

    I'm working under the assumption that we're using Ubuntu or a Debian-based Linux distro. The first thing we need to do is remove Node:

    $ apt remove node
    $ apt remove nodejs
    

    Verify that Node is gone:

    $ which node
    

    If a folder path is returned by the above, nuke it. That should leave us ready for a fresh install.

    Since we're on a Debian system, we need to install Node via Nodesource. I'd recommend running this in the /tmp folder, or wherever you like to dump things saved from the internet:

    $ curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
    $ apt-get install -y nodejs
    

    Now would be a good time to get the latest npm as well:

    $ npm i -g [email protected]
    

    When the installation is complete, check to see if everything worked:

    $ node -v
    v10.15.2
    
    $ npm -v
    6.9.0
    

    Creating a New Global NPM Directory

    With Node reinstalled, we can move on to creating and configuring a new directory to hold our global npm modules. You can name this what you want, but it's generally recommended to name this npm-global or something of the sort. Don't use sudo!

    $ mkdir ~/.npm-global
    

    Now we can configure npm to recognize this folder:

    $  npm config set prefix '~/.npm-global'
    

    Next, we need to add this to our PATH. This should go in ~/.bashrc or ~/.bash_profile, depending on which you use:

    $  echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
    $  source ~/.bashrc
    

    You Did Good

    That should do it! From now on, remember to stray away from using sudo when installing Node, npm, or even modifying npm packages. The npm documentation explicitly recommends against this, but it's all good. We all make mistakes.

    Todd Birchard's' avatar
    New York City Website
    Product manager turned engineer with an ongoing identity crisis. Breaks everything before learning best practices. Completely normal and emotionally stable.

    Product manager turned engineer with an ongoing identity crisis. Breaks everything before learning best practices. Completely normal and emotionally stable.