Carling Knight
Carling Knight

Reputation: 495

Webpacked lambda throws ERR_INVALID_ARG_TYPE(name, 'string', value); when ran

Attempting to create a JavaScript lambda that is bundled using webpack. My current index.js has a structure like this:

const AWS = require('aws-sdk');
const fs = require('fs');

exports.handler = async (event) => {
...
}

And my webpack.config.js looks like:

const CopyPlugin = require('copy-webpack-plugin');
const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
    entry: './index.js',
    output: {
        path: path.resolve(__dirname, './build'),
        filename: 'index.js',
        libraryTarget: "commonjs"
    },
    mode: 'development',
    target: "node",
    plugins: [
        new CopyPlugin([
            { from: './deployment.xsd', to: './deployment.xsd'}
        ])
    ],
    externals: [ nodeExternals() ]
};

Webpack runs fine and returns a happy result, that looks like:

Hash: d5110295ceffea09db88
Version: webpack 4.41.2
Time: 72ms
Built at: 11/12/2019 4:12:47 PM
         Asset      Size  Chunks             Chunk Names
deployment.xsd  24.4 KiB          [emitted]  
      index.js  7.08 KiB    main  [emitted]  main
Entrypoint main = index.js
[./index.js] 2.16 KiB {main} [built]
[aws-sdk] external "aws-sdk" 42 bytes {main} [built]
[fs] external "fs" 42 bytes {main} [built]
[libxmljs] external "libxmljs" 42 bytes {main} [built]

I then package this up using Maven which bundles it into a zip.

But if I attempt to run it locally using node-lambda run -H 'build/index.js' -j test/test.json, I get the following error:

/.../node_modules/continuation-local-storage/context.js:55
    throw exception;
    ^

TypeError: handler is not a function
    at /.../node_modules/node-lambda/lib/main.js:124:22
    at Namespace.run (/.../node_modules/continuation-local-storage/context.js:48:5)
    at Lambda._runHandler (/.../node_modules/node-lambda/lib/main.js:122:15)
    at Lambda.run (/.../node_modules/node-lambda/lib/main.js:83:10)
    at Command.<anonymous> (/.../node_modules/node-lambda/bin/node-lambda:143:27)
    at Command.listener (/.../node_modules/commander/index.js:315:8)
    at Command.emit (events.js:203:13)
    at Command.EventEmitter.emit (domain.js:471:20)
    at Command.parseArgs (/.../node_modules/commander/index.js:651:12)
    at Command.parse (/.../node_modules/commander/index.js:474:21) {
  'error@context': [Object: null prototype] {
    segment: Segment {
      trace_id: '1-5dcada48-a2ce6baf341247ac09d71c53',
      id: 'e75d048edf0d1b8d',
      start_time: 1573575240.157,
      name: 'annotations',
      in_progress: true,
      counter: 0,
      service: [Object],
      aws: [Object]
    }
  }
}

Managed to resolve this by using node-lambda run -H 'build/index.handler, seems the bit after the dot on that line is the handler name, not the file extension.

And on AWS I get the following error, which is moaning about a library I use that hasn't been packaged up:

{
  "errorType": "Runtime.ImportModuleError",
  "errorMessage": "Error: Cannot find module 'libxmljs'",
  "trace": [
    "Runtime.ImportModuleError: Error: Cannot find module 'libxmljs'",
    "    at _loadUserApp (/var/runtime/UserFunction.js:100:13)",
    "    at Object.module.exports.load (/var/runtime/UserFunction.js:140:17)",
    "    at Object.<anonymous> (/var/runtime/index.js:45:30)",
    "    at Module._compile (internal/modules/cjs/loader.js:778:30)",
    "    at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)",
    "    at Module.load (internal/modules/cjs/loader.js:653:32)",
    "    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)",
    "    at Function.Module._load (internal/modules/cjs/loader.js:585:3)",
    "    at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)",
    "    at startup (internal/bootstrap/node.js:283:19)"
  ]
}

What's the error here? A snippet from build/index.js

(function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap
/******/    // The module cache
/******/    var installedModules = {};
/******/
/******/    // The require function
/******/    function __webpack_require__(moduleId) {
/******/
/******/        // Check if module is in cache
/******/        if(installedModules[moduleId]) {
/******/            return installedModules[moduleId].exports;
/******/        }
/******/        // Create a new module (and put it into the cache)
/******/        var module = installedModules[moduleId] = {
/******/            i: moduleId,
/******/            l: false,
/******/            exports: {}
/******/        };
/******/
/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/        // Flag the module as loaded
/******/        module.l = true;
/******/
/******/        // Return the exports of the module
/******/        return module.exports;
/******/    }
/******/
/******/
/******/    // expose the modules object (__webpack_modules__)
/******/    __webpack_require__.m = modules;
/******/
/******/    // expose the module cache
/******/    __webpack_require__.c = installedModules;
/******/
/******/    // define getter function for harmony exports
/******/    __webpack_require__.d = function(exports, name, getter) {
/******/        if(!__webpack_require__.o(exports, name)) {
/******/            Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/        }
/******/    };
/******/
/******/    // define __esModule on exports
/******/    __webpack_require__.r = function(exports) {
/******/        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/        }
/******/        Object.defineProperty(exports, '__esModule', { value: true });
/******/    };
/******/
/******/    // create a fake namespace object
/******/    // mode & 1: value is a module id, require it
/******/    // mode & 2: merge all properties of value into the ns
/******/    // mode & 4: return value when already ns object
/******/    // mode & 8|1: behave like require
/******/    __webpack_require__.t = function(value, mode) {
/******/        if(mode & 1) value = __webpack_require__(value);
/******/        if(mode & 8) return value;
/******/        if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/        var ns = Object.create(null);
/******/        __webpack_require__.r(ns);
/******/        Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/        if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/        return ns;
/******/    };
/******/
/******/    // getDefaultExport function for compatibility with non-harmony modules
/******/    __webpack_require__.n = function(module) {
/******/        var getter = module && module.__esModule ?
/******/            function getDefault() { return module['default']; } :
/******/            function getModuleExports() { return module; };
/******/        __webpack_require__.d(getter, 'a', getter);
/******/        return getter;
/******/    };
/******/
/******/    // Object.prototype.hasOwnProperty.call
/******/    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";
/******/
/******/
/******/    // Load entry module and return exports
/******/    return __webpack_require__(__webpack_require__.s = "./index.js");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./index.js":
/*!******************!*\
  !*** ./index.js ***!
  \******************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

eval("/* WEBPACK VAR INJECTION */(function(__dirname) {const AWS = __webpack_require__(/*! aws-sdk */ \"aws-sdk\");\nconst libxmljs = __webpack_require__(/*! libxmljs */ \"libxmljs\");\nconst fs = __webpack_require__(/*! fs */ \"fs\");\n\nexports.handler = async (event) => {

The other method we've tried is to remove this line from the webpack.config.js, this changes the error and the compiled index.js is much bigger as it actually has the dependencies included, but then we see this error both locally and on AWS:

internal/validators.js:107
    throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
    ^

TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined
    at validateString (internal/validators.js:107:11)
    at dirname (path.js:1121:5)
    at Function.getRoot (webpack:///./node_modules/bindings/bindings.js?:154:13)
    at bindings (webpack:///./node_modules/bindings/bindings.js?:60:32)
    at eval (webpack:///./node_modules/libxmljs/lib/bindings.js?:1:92)
    at Object../node_modules/libxmljs/lib/bindings.js (/.../build/index.js:8971:1)
    at __webpack_require__ (/.../build/index.js:20:30)
    at eval (webpack:///./node_modules/libxmljs/index.js?:4:16)
    at Object../node_modules/libxmljs/index.js (/.../build/index.js:8960:1)
    at __webpack_require__ (/.../build/index.js:20:30)

Upvotes: 0

Views: 1527

Answers (1)

Rajan Panneer Selvam
Rajan Panneer Selvam

Reputation: 1299

The libxmljs library is written in C. And webpack cannot pack this into a js file to be executed in a browser.

You are seeing this error because libxmljs is not packaged. You have to find a Pure JS version of this library and use it if you want to use webpack.

However, I will not prefer using webpack builds for lambda functions. Every language supported by Lamda has to manage External dependencies. And AWS provides better support for this. So zip all your nodejs contents and upload them to Lambda. It will be sufficient in most cases.

Additionally, you can leverage Lambda Layers, https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html. With Layers, your node_modules can be in a separate layer.

Also, you may need to install the npm modules in an Amazon Linux machine/another compatible machine (https://github.com/kelektiv/node.bcrypt.js/issues/505).

Upvotes: 1

Related Questions