JavaScript ポリシーの実行時エラーのトラブルシューティング

現在、ApigeeApigee ハイブリッドのドキュメントを表示しています。
Apigee Edge のドキュメントを表示する。

ScriptExecutionFailed

エラーコード

steps.javascript.ScriptExecutionFailed

エラー レスポンスの本文

{
    "fault": {
        "faultstring": "Execution of javascript_policy_name failed with error: error_type: error_description. (javascript_source_file_name)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

エラーの種類と考えられる原因

JavaScript ポリシーにより、さまざまな種類の ScriptExecutionFailed エラーがスローされる可能性があります。遭遇することの多いエラーを以下の表に示します。

エラーの種類 原因
範囲エラー RangeError は、正当な値の範囲外の数値を使用するとスローされます。
参照エラー ReferenceError は、宣言されていない変数を使用(参照)するとスローされます。
構文エラー SyntaxError は、構文エラーでコードを評価しようとするとスローされます。
型エラー TypeError は、予期されるタイプの範囲外のオペレーションを使用するとスローされます。
URI エラー URIError は、URI 関数内で不正な文字を使用するとスローされます。

範囲エラー

使用できる値のセットまたは範囲に含まれない値を操作するか、関数に渡すと、エラータイプ RangeError がスローされます。

たとえば、このエラーは以下のような状況でスローされます。

  1. 2018 年 9 月 31 日などの無効な日付を Date API で使用した場合。
  2. Number.toPrecision()Number.tofixed()Number.toExponential() などの数値メソッドに無効な値を渡した場合。たとえば、Number.toPrecision() メソッドで 400 や 500 などの大きな値を渡すと、範囲エラーが発生します。
  3. 不正な長さで配列を作成した場合。

エラー レスポンスの本文

{
    "fault": {
        "faultstring": "Execution of javascript_policy_name failed with error: Javascript runtime error: \"RangeError: error_description. (javascript_source_file_name:line_number)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

注: 範囲エラーの診断と問題解決は、JavaScript ポリシーによってスローされたエラー メッセージが正確にはどのようなものかによって異なります。参考のため、いくつか例を挙げて説明します。

例 1: 無効な日付

エラー レスポンスの本文の例

{
    "fault": {
        "faultstring": "Execution of ParseDate failed with error: Javascript runtime error: \"RangeError: Date is invalid. (ParseDate.js:2)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

診断

  1. JavaScript ポリシー、そのソースファイル、エラーが発生した行番号、エラーの説明を特定します。これらの情報はすべて、エラー レスポンスの faultstring 要素で調べることができます。たとえば、次の faultstring では、JavaScript ポリシー名は ParseDate、JavaScript ソースファイルは ParseDate.js、エラーの行番号は 2、エラーの説明は Date is invalid です。

    "faultstring": "Execution of ParseDate failed with error: Javascript runtime error: \"RangeError: Date is invalid. (ParseDate.js:2)\""
    
  2. JavaScript ソースファイル(上記のステップ 1 で特定したもの)を調べ、エラーで指摘された行番号で不適切な日付が使用されていないかどうか、または行番号で使用されている変数に無効な日付が含まれていないかどうかを確認してください。無効な日付が使われていれば、それがエラーの原因です。

    このエラーを引き起こすサンプル JavaScript ソースファイルを次に示します。

    ParseDate.js

    var date = new Date('2018-09-31T11:19:08.402Z');
    date.toISOString();
    

    この例では、行番号 2 で使用されている変数 date があります。ソースファイルを調べると、date 変数に無効な日付(2018-09-31T11:19:08.402Z.)が設定されていることがわかります。9 月には 31 日がないため、この日付は無効です。

    注: この例で使用されている ISO-8601 形式は、YYYY-MM-DDTHH:mm:ss.sssZ です。

解決策

JavaScript コードで Date API を使うときは、必ず有効な日付を使うようにします。

上記のサンプル JavaScript コードを修正するには、次のように日付に Sept 30 2018 を設定します。

var date = new Date('2018-09-30T11:19:08.402Z');
date.toISOString();

例 2: Precision API に不正な数値を渡した

エラー レスポンスの本文の例

{
    "fault": {
        "faultstring": "Execution of SetNumberPrecision failed with error: Javascript runtime error: "RangeError: Precision 400 out of range. (SetNumberPrecision.js:2)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

診断

  1. JavaScript ポリシー、そのソースファイル、エラーが発生した行番号、エラーの説明を特定します。これらの情報はすべて、エラー レスポンスの faultstring 要素で調べることができます。たとえば、次の faultstring では、JavaScript ポリシー名は SetNumberPrecision、JavaScript ソースファイルは SetNumberPrecision.js、発生したエラーは 2、エラーの説明は Precision 400 out of range. です。

    "faultstring": "Execution of SetNumberPrecision failed with error: Javascript runtime error: "RangeError: Precision 400 out of range. (SetNumberPrecision.js:2)\""
    
  2. JavaScript ソースファイル(上記のステップ 1 で特定したもの)を調べます。エラーの説明で示された大きな数値が、特定の行番号で使われていれば、それがエラーの原因です。

    このエラーを引き起こすサンプル JavaScript ソースファイルを次に示します。

    SetNumberPrecision.js

    var number = 12.3456;
    var rounded_number = number.toPrecision(400);
    print("rounded_number = " + rounded_number);
    

    上の例では、行番号 2 で 400 という大きな値が使われています。このような大きな桁数の数字を Precision では設定できないため、次のエラーが発生します。

    "faultstring": "Execution of SetNumberPrecision failed with error: Javascript runtime error: "RangeError: Precision 400 out of range. (SetNumberPrecision.js:2)\""
    

解決策

toPrecision() メソッドで使用されている数値が、使用できる値の範囲内にあることを確認します。

上記のサンプル JavaScript の問題を解決するには、次のように有効桁数を 2 桁に設定します。

var number = 12.3456;
var rounded_number = number.toPrecision(2);
print("rounded_number = " + rounded_number);

参照エラー

エラータイプ ReferenceError は、JavaScript で定義されていない変数が使用(参照)または操作されたときにスローされます。

エラー レスポンスの本文

{
    "fault": {
        "faultstring": "Execution of javascript_policy_name failed with error: Javascript runtime error: \"ReferenceError: variable_name is not defined. (javascript_source_file_name:line_number)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

エラー レスポンスの本文の例

{
    "fault": {
        "faultstring": "Execution of ComputeTotalPrice failed with error: Javascript runtime error: \"ReferenceError: \"price\" is not defined. (ComputeTotalPrice.js:3)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

診断

  1. JavaScript ポリシー、そのソースファイル、未定義の変数が参照された行番号を特定します。これらの情報はすべて、エラー レスポンスの faultstring 要素で調べることができます。たとえば、次の faultstring では、JavaScript ポリシー名は ComputeTotalPrice、対応するソースファイルは ComputeTotalPrice.js、エラーが発生した行番号は 3、未定義の変数名は price. です。

    "faultstring": "Execution of ComputeTotalPrice failed with error: Javascript runtime error: \"ReferenceError: \"price\" is not defined. (ComputeTotalPrice.js:3)\""
    
  2. JavaScript ソースファイルで行番号を調べ、上のステップ 1 で特定した未定義の変数が参照されていないかどうかを確認します。たとえば、次の JavaScript コードは、3 行目の未定義の変数 price を参照しています。これは、faultstring の内容と一致します。

    ComputeTotalPrice.js

    var item = context.getVariable("request.queryparam.item");
    var quantity = context.getVariable("request.queryparam.quantity");
    var totalprice = parseInt(quantity) * parseInt(price);
    context.setVariable("TotalPrice", totalprice);
    
    
  3. その特定の変数が JavaScript コード内で定義されているかどうかを確認します。その変数が定義されていない場合、それがエラーの原因です。

    上記のサンプル スクリプトでは、宣言されていない、または未定義の price 変数が使用されています。そのため、次のようなエラーが表示されます。

    "faultstring": "Execution of ComputeTotalPrice failed with error: Javascript runtime error: \"ReferenceError: \"price\" is not defined. (ComputeTotalPrice.js:3)\""
    

解決策

JavaScript コードで参照されるすべての変数が正しく定義されていることを確認します。

上記のサンプル JavaScript の問題を解決するには、変数 price を使用する前に定義します。次に例を示します。

var item = context.getVariable("request.queryparam.item");
var quantity = context.getVariable("request.queryparam.quantity");
var price = context.getVariable("request.queryparam.price");
var totalprice = parseInt(quantity) * parseInt(price);
context.setVariable("TotalPrice", totalprice);

構文エラー

エラータイプ SyntaxError は、JavaScript エンジンが言語の構文に準拠しないトークンまたはトークンの順序に遭遇した場合、または JSON/XML 解析のようなパーサー API に無効な形式の入力が渡されたときにスローされます。

たとえば、JavaScript ポリシー内で使用される JSON.parse API に入力として無効または不適切な形式の JSON ペイロードが渡されると、このエラーが発生します。

エラー レスポンスの本文

{
    "fault": {
        "faultstring": "Execution of javascript_policy_name failed with error: Javascript runtime error: \"SyntaxError: error_description. (javascript_source_file_name:line_number)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

エラー レスポンスの本文の例

{
    "fault": {
        "faultstring": "Execution of ParseJSONRequest failed with error: Javascript runtime error: \"SyntaxError: Unexpected token: <. (ParseJSONRequest.js:2)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

診断

  1. JavaScript ポリシー、そのソースファイル、エラーが発生した行番号、エラーの説明を特定します。これらの情報はすべて、エラー レスポンスの faultstring 要素で調べることができます。たとえば、次の faultstring では、JavaScript ポリシー名は ParseJSONRequest、JavaScript ソースファイルは ParseJSONRequest.js、エラーの行番号は 2、エラーの説明は Unexpected token です。

    "faultstring": "Execution of ParseJSONRequest failed with error: Javascript runtime error: \"SyntaxError: Unexpected token: <. (ParseJSONRequest.js:2)\""
    
  2. 上のステップ 1 で特定した JavaScript ソースファイルの 2 行目を調べ、オペレーションが実行されているかどうかを確認します。JSON.parse() 関数が実行されている場合は、渡された入力パラメータを確認します。入力パラメータが無効あるいは不正な形式の JSON の場合、それがエラーの原因です。

    このエラーを引き起こすサンプル JavaScript コードを次に示します。

    var input = context.getVariable("request.content");
    var result = JSON.parse(input);
    

    この例では、API プロキシに渡されるリクエスト ペイロード(request.content)が JSON.parse() 関数への入力として使用されています。

    次の API 呼び出しの例は、リクエストがどのように渡されたかを示しています。

    curl -v "http://<org>-<env>.apigee.net/v1/js-demo" -H "Content-Type: application/json" -X POST -d '<city>Bangalore</city>'
    

    上記のリクエストでは、次の XML ペイロードが API プロキシ <city>Bangalore</city> に渡されます。JSON.parse API では有効な JSON が渡されることが想定されていますが、代わりに XML ペイロードが渡されるため、次のエラーで失敗します。

    "Execution of ParseJSONRequest failed with error: Javascript runtime error: \"SyntaxError: Unexpected token: <. (ParseJSONRequest.js:2)\""
    

解決策

JavaScript コードで使われている解析 API には必ず有効な入力を渡すようにします。

上記のサンプル ポリシーの問題を解決するには、次のように有効な JSON ペイロード リクエストを渡します。

curl -v "http://<org>-<env>.apigee.net/v1/js-demo" -H "Content-Type: application/json" -X POST -d '{"city" : "Bangalore"}'

型エラー

エラータイプ TypeError は、次の場合にスローされます。

  • 関数に渡されるオペランドまたは引数が、その演算子または関数で想定されている型と互換性がない場合。
  • null、未定義のオブジェクト、間違ったオブジェクトに対して関数が呼びだされた場合。
  • null、未定義のオブジェクト、間違ったオブジェクトからプロパティにアクセスされた場合。

たとえば、型エラーは次の場合にスローされます。

  • 数値に対して toUpperCase() 関数を呼び出そうとした場合。これは、toUpperCase() 関数が文字列オブジェクトに対してのみ呼び出し可能なためです。
  • null または未定義のオブジェクトからプロパティを読み取ろうとした場合。

エラー レスポンスの本文

{
    "fault": {
        "faultstring": "Execution of javascript_policy_name failed with error: Javascript runtime error: \"TypeError: error_description. (javascript_source_file_name:line_number)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

例 1: 誤ったオブジェクトに対する関数の呼び出し

サポートされていないオブジェクトで関数を呼び出そうとすると、このエラーが発生します。たとえば、数値に対して toUpperCase() 関数を呼び出そうとすると、エラーが発生します。これは、toUpperCase() 関数が文字列オブジェクトに対してのみ呼び出し可能なためです。

エラー レスポンスの本文の例

{
    "fault": {
        "faultstring": "Execution of ConvertToUpperCase failed with error: Javascript runtime error: \"TypeError: Cannot find function toUpperCase in object 100. (ConvertToUpperCase.js:2)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

診断

  1. JavaScript ポリシー、そのソースファイル、エラーが発生した行番号、エラーの説明を特定します。これらの情報はすべて、エラー レスポンスの faultstring 要素で調べることができます。たとえば、次の faultstring では、JavaScript ポリシー名は ConvertToUpperCase、JavaScript ソースファイルは ConvertToUpperCase.js、行番号は 2、エラーの説明は **Cannot find function toUpperCase in object 100. です。

    "faultstring": "Execution of ConvertToUpperCase failed with error: Javascript runtime error: \"TypeError: Cannot find function toUpperCase in object 100. (ConvertToUpperCase.js:2)\""
    

    エラーの説明は、数値が 100 のオブジェクトに対して関数 toUpperCase() を呼び出していることを示しています。

  2. JavaScript ソースファイルを調べ、行番号 2(上記のステップ 1 で特定)で数値が 100 のオブジェクトに対して toUpperCase() 関数を呼び出していないかどうかを確認します。呼び出している場合は、それがエラーの原因です。

    このエラーを引き起こすサンプル JavaScript ソースファイルを次に示します。

    ConvertToUpperCase.js

    var number = 100;
    var result = number.toUpperCase();
    

    上記の JavaScript コードでは、変数 number の値は 100 に設定されています。その後、数値オブジェクトに対して toUpperCase()( 関数が呼び出されます。toUpperCase() 関数は文字列オブジェクトに対してのみ呼び出し可能なため、次のエラーが発生します。

    "Execution of ConvertToUpperCase failed with error: Javascript runtime error: \"TypeError: Cannot find function toUpperCase in object 100. (ConvertToUpperCase.js:2)\""
    

解決策

toUpperCase() などの関数は、有効なオブジェクトに対して使用するようにしてください。

上記の例を修正するには、文字列変数を作成し、文字列に対して toUpperCase() 関数を呼び出します。

var text = "Hello Apigee !";
var result = text.toUpperCase();

例 2: 未定義のオブジェクトからプロパティを読み込めない

未定義オブジェクトからプロパティにアクセスまたは読み取りしようとすると、このエラーが発生します。たとえば、配列内のオブジェクトからデータにアクセスしようとしても、そのオブジェクトが未定義である場合、このエラーが発生します。詳しくは、以下の説明をご覧ください。

エラー レスポンスの本文の例

{
    "fault": {
        "faultstring": "Execution of ParseJSONResponse failed with error: Javascript runtime error: \"TypeError: Cannot read property \"length\" from undefined. (ParseJSONResponse.js:7)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

診断

  1. JavaScript ポリシー、そのソースファイル、エラーが発生した行番号、エラーの説明を特定します。これらの情報はすべて、エラー レスポンスの faultstring 要素で調べることができます。たとえば、次の faultstring では、ポリシー名は ParseJSONResponse、ソースファイルは ParseJSONResponse.js、行番号は 6、エラーの説明は Cannot read property "length" from undefined です。

    "faultstring": "Execution of ParseJSONResponse failed with error: Javascript runtime error: \"TypeError: Cannot read property \"length\" from undefined. (ParseJSONResponse.js:6)\""
    

    このエラーは、未定義のオブジェクトからプロパティ length が読み取れないことを示しています。

  2. 上のステップ 1 で特定した JavaScript ソースファイルの行を調べ、オブジェクトが有効な値を持つか、あるいは未定義であるかどうかを確認します。そのオブジェクトがどのように定義されたか、どのようにして得られたかを特定し、オブジェクトが定義されていないと判断できる理由を確定するには、ソースファイル全体を読んで理解する必要があります。そのオブジェクトが確かに未定義であって、そこからプロパティ length にアクセスしようとしているなら、それがエラーの原因です。

    この問題を詳細に理解するために、以下の例を見てみましょう。

    1. バックエンド サーバーから次の JSON レスポンスを受け取ったとします。

      {
          "cars": [
             { "name":"Toyota", "count": 150 }
             { "name":"Honda", "count": 100 },
             { "name":"Ford", "count": 75 }
          ]
      }
      
    2. 次に、上記のエラーにつながる、この JSON レスポンスを解析する JavaScript ソースファイルの例を示します。

      ParseJSONResponse.js

      // Get the JSON response
      var jsonData = context.getVariable("response.content");
      print (jsonData);
      
      // Read the cars array
      for (var i = 0; i < jsonData.cars.length; i++)
        {
        print("name = " + jsonData.cars[i].name);
        print("count = " + jsonData.cars[i].count);
        }
      
    3. JavaScript コードを慎重に調べると、2 行目で jsonData 変数に対して(引用符で囲んだ)通常の文字列として response.content が読み取り/保存されていることがわかります。

    4. jsonData は通常の文字列であるため、jsonData から cars にアクセスしようとすると、(jsonData.cars)は未定義になります。

    5. その後、未定義の jsonData.cars のプロパティ length を読み取ろうとすると、次のエラーが発生します。

      "faultstring": "Execution of ParseJSONResponse failed with error: Javascript runtime error: \"TypeError: Cannot read property \"length\" from undefined. (ParseJSONResponse.js:6)\""
      

解決策

必ず、該当する JSON API を使って、JSON データを常に JSON オブジェクトとして読み取るようにしてください。

上記の JavaScript の例を修正するには、response.content オブジェクトで JSON.parse() 関数を使用して JSON オブジェクトとして取得します。その後、cars 配列にアクセスすれば、配列を正しく反復処理できます。

// Get the JSON response
var data = context.getVariable("response.content");
var jsonData = JSON.parse(data);
print (jsonData);

// Read the cars array
for (var i = 0; i < jsonData.cars.length; i++)
{
    print("name = " + jsonData.cars[i].name);
    print("count = " + jsonData.cars[i].count);
}

URI エラー

URI 関数で無効な文字を使用すると、エラータイプ URIError がスローされます。たとえば、パーセント記号を含む URI を decodeURI 関数や decodeURIComponent 関数に渡すと、このエラーが発生します。

エラー レスポンスの本文

{
    "fault": {
        "faultstring": "Execution of javascript_policy_name failed with error: Javascript runtime error: \"URIError: error_description. (javascript_source_file_name:line_number)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

エラー レスポンスの本文の例

{
    "fault": {
        "faultstring": "Execution of URIDecode failed with error: Javascript runtime error: \"URIError: Malformed URI sequence. (URIDecode.js:2)\"",
        "detail": {
            "errorcode": "steps.javascript.ScriptExecutionFailed"
        }
    }
}

診断

  1. JavaScript ポリシー、そのソースファイル、エラーが発生した行番号、エラーの説明を特定します。これらの情報はすべて、エラー レスポンスの faultstring 要素で調べることができます。たとえば、次の faultstring では、JavaScript ポリシー名は URIDecode</code、JavaScript ソースファイルは URIDecode.js、行番号は 2、エラーの説明は Malformed URI sequence です。

    "faultstring": "Execution of URIDecode failed with error: Javascript runtime error: \"URIError: Malformed URI sequence. (URIDecode.js:2)\""
    

    エラーの説明では、URIDecode.js の 2 行目で不正な形式の URI シーケンスが使用されていることが示されています。

  2. JavaScript ソースファイルを調べ、不正な文字を含む引数が渡された URI 関数がないかどうかを確認します。もしあるならば、それがエラーの原因です。

    このエラーを引き起こす JavaScript ソースファイルの例を次に示します。

    URIDecode.js

    var str = "75%-Completed";
    var decoded_str = decodeURIComponent(str);
    context.setVariable("decoded_str", decoded_str);
    

    上記の JavaScript サンプルコードでは、decodeURIComponent() に渡される変数 str にパーセントの記号があり、これは不正な文字と見なされます。そのため、次のエラーが発生します。

    "Execution of URIDecode failed with error: Javascript runtime error: \"URIError: Malformed URI sequence. (URIDecode.js:2)\""
    

解決策

URI 関数には、決められた文字、許されている文字以外を渡さないようにします。

上記のサンプル JavaScript の問題を解決するには、パーセントの記号をエンコードします。例: %25

var str = "75%25-Completed";
var decoded_str = decodeURIComponent(str);
context.setVariable("decoded_str", decoded_str);