When writing to console inside catch in a Google Cloud Function nothing is written - exception

I have an array of work to do inside a Google Cloud Function. If I run the script locally it produces output to console but not in the cloud function.
// this is logged
console.error('An error');
for (var i=0; i<chunks.length; i++) {
const work = chunks[i].map(c => createOrUpdateTable(c, timestamp));
await Promise.all(work)
// this is not logged
.catch(e => console.error(e.message));
}
I've tried to put the catch inside the function and a whole lot of other things but same behaviour. How can I get the error to appear in the log?

According to the official documentation, you can emit an error from a Cloud Function to
Stackdriver Error Reporting:
// These WILL be reported to Stackdriver Error Reporting
console.error(new Error('I failed you'));
console.error('I failed you', new Error('I failed you too'));
throw new Error('I failed you'); // Will cause a cold start if not caught
// These will NOT be reported to Stackdriver Error Reporting
console.info(new Error('I failed you')); // Logging an Error object at the info level
console.error('I failed you'); // Logging something other than an Error object
throw 1; // Throwing something other than an Error object
callback('I failed you');
res.status(500).send('I failed you');
Reporting Errors

You should use a try/catch block as follows:
for (var i=0; i<chunks.length; i++) {
const work = chunks[i].map(c => createOrUpdateTable(c, timestamp));
try {
await Promise.all(work)
} catch (e) {
console.error(e.message));
return { error: err }
}
}

I have reproduced the issue and I get the error logged but differently.
First function:
exports.helloError = (data, context, callback) => {
// [START functions_helloworld_error]
// These WILL be reported to Stackdriver Error Reporting
console.error(new Error('I failed you'));
console.error('I failed you', new Error('I failed you too'));
throw new Error('I failed you'); // Will cause a cold start if not caught
// [END functions_helloworld_error]
};
In stackdriver logging it appears as an error !!
severity: "ERROR"
textPayload: "Error: I failed you
at exports.helloError (/srv/index.js:4:17)
at /worker/worker.js:783:7
at /worker/worker.js:766:11
And with the second function:
exports.helloError = (data, context, callback) => {
try{
throw new Error('I failed you'); // Will cause a cold start if not caught
}catch(e){
console.log(e.message);
};
};
It is reported as an INFO λ
severity: "INFO"
textPayload: "I failed you"
I suspect that as the error is handled in the second one, the function is working as expected, so it will not be reported as an error but information of the performance.

Related

How to properly use revert reason in web3.js to show meaningful error message in UI

I want to use web3.js to show revert reason to user, for example in the case of user trying to mint erc721 token that has already been minted. I am using try catch block and see the error message but I want to isolate the error message to show the user a meaningful reason. Thanks in advance.
The previous answer by #Petr Hejda didn't work for me, and neither did his suggestion in response to #Chakshu Jain's problem in the comments.
Instead, I removed some characters—from the start and the end, with slice()—that were causing the error when parsing the JSON, so I could handle the error message and get the error message.
if (err) {
var errorMessageInJson = JSON.parse(
err.message.slice(58, err.message.length - 2)
);
var errorMessageToShow = errorMessageInJson.data.data[Object.keys(errorMessageInJson.data.data)[0]].reason;
alert(errorMessageToShow);
return;
}
It's returned in the JS error object as data.<txHash>.reason.
This is a faulty Solidity code
pragma solidity ^0.8.0;
contract Test {
function foo() public {
revert('This is error message');
}
}
So a transaction calling the foo() function should revert with the message This is error message.
try {
await myContract.methods.foo().send();
} catch (e) {
const data = e.data;
const txHash = Object.keys(data)[0]; // TODO improve
const reason = data[txHash].reason;
console.log(reason); // prints "This is error message"
}
After trying out every solution on stackoverflow, random blogs, and even the officially documented "web3.eth.handleRevert = true", none is working for me.
I finally figured out after 25 failed attempts:
try {
await obj.methods.do_something().call({
gasLimit: String(GAS_LIMIT),
to: CONTRACT_ADDRESS,
from: wallet,
value: String(PRICE),
})
}
catch (err) {
const endIndex = err.message.search('{')
if (endIndex >= 0) {
throw err.message.substring(0, endIndex)
}
}
try {
const res = await obj.methods.do_something().send({
gasLimit: String(GAS_LIMIT),
to: CONTRACT_ADDRESS,
from: wallet,
value: String(PRICE),
})
return res.events.Transfer.returnValues.tokenId
}
catch (err) {
console.error(err)
throw err
}
The idea is to use call first. This method doesn't interact with your Metamask, but merely checks if your input arguments go through the contract method. If it can't go through, it will throw exception in the first catch block. If it does go through, we are safe to do use send. This method interacts with your Metamask for real. We have a second catch block in case there are wallet connection or gas fee issues
It is really perplexing why Solidity/Web3 don't have an easy way to extract the require/revert reason from the error object.
For me, the "require" reason is there in the message property of the error object, but it is surrounded by lot of other words which I don't need.
An example error message:
[ethjs-query] while formatting outputs from RPC '{"value":{"code":-32603,"data":{"message":"VM Exception while processing transaction: revert Voting is closed","code":-32000,"data":{"0xf901429f12096d3b5c23a80e56fd2230fa37411bb1f8d3cdbd5c8f91c2670771":{"error":"revert","program_counter":43,"return":"0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000165f5f5f566f74696e6720697320636c6f7365645f5f5f00000000000000000000","reason":"Voting is closed"},"stack":"RuntimeError: VM Exception while processing transaction: revert Voting is closed \n at Function.RuntimeError.fromResults (/tmp/.mount_ganachreY1gT/resources/static/node/node_modules/ganache-core/lib/utils/runtimeerror.js:94:13)\n at BlockchainDouble.processBlock (/tmp/.mount_ganachreY1gT/resources/static/node/node_modules/ganache-core/lib/blockchain_double.js:627:24)\n at runMicrotasks (<anonymous>)\n at processTicksAndRejections (internal/process/task_queues.js:93:5)","name":"RuntimeError"}}}}'
You can see the reason Voting is closed stuck in between. Not that user-friendly to read.
I've seen answers that use regex to extract the error reason.
For those like me, who are not a big fan of the regex way, here is my approach.
In your solidity contract, wrap the require reason with a unique delimiter of sorts. In my case, it is "___" (3 underscores).
contract MyContract{
...
...
function vote(address _addr) public payable{
require(votingOpen, "___Voting closed___");
...
}
...
...
}
Declare a helper function to extract the error using JavaScript string utilities. Here's where your delimiter coes in handy.
export const extractErrorCode = (str) => {
const delimiter = '___'; //Replace it with the delimiter you used in the Solidity Contract.
const firstOccurence = str.indexOf(delimiter);
if(firstOccurence == -1) {
return "An error occured";
}
const secondOccurence = str.indexOf(delimiter, firstOccurence + 1);
if(secondOccurence == -1) {
return "An error occured";
}
//Okay so far
return str.substring(firstOccurence + delimiter.length, secondOccurence);
}
Use this function where you catch the error in your frontend
const vote = async (_addr) => {
setLoading(true);
try {
await contest.methods.vote(_addr).send({
from: accounts[0],
})
}
catch (e) {
console.log('Voting failed with error object => ', e)
console.log('Voting failed with the error => ', extractErrorCode(e.message))
}
setLoading(false);
}
Until Solidity & Web3.js (and ether.js) come out with a clean way to parse errors, we are stuck with workarounds like this.
I prefer this workaround over others because I am not that great with regex, and additionally, this one does not depend on a fixed starting position to extract the error code.
Did you try something like this?
error.toString()
It works for me just to show the revert error in the Smart Contract, and return it as a string message.
try {
//Do something
} catch (error) {
res.send({
'status': false,
'result': error.toString()
});
}

How to throw a future through waitFor in Dart

I'm the author of the Dart dshell package.
https://pub.dev/packages/dshell
Dshell is a library and tooling for writing dart cli scripts.
Dshell uses waitFor to hide futures from users as they serve little use in the typical cli application.
My problem is that if a future throws an unhandled exception whilst being handled by waitFor, it essentially shuts the application down.
I need to be able to capture any exception and then let the caller decided what to do with the exception.
Here is what I've tried so far. No of the catch techniques will capture the unhandled exception:
import 'dart:async';
import 'dart:cli';
void main() {
var future = throwException();
try {
future
.catchError((Object e, StackTrace st) => print('onErrr: $e'))
.whenComplete(() => print('future completed'));
print(waitFor<int>(future));
} // on AsyncError
catch (e) {
if (e.error is Exception) {
print(e.error);
} else if (e is AsyncError) {
print('Rethrowing a non DShellException ${e}');
rethrow;
} else {
print('Rethrowing a non DShellException ${e}');
rethrow;
}
} finally {
print('waitForEx finally');
}
}
Future<int> throwException() {
var complete = Completer<int>();
Future.delayed(Duration(seconds: 2), () => throw Exception());
return complete.future;
}
The dart waitFor has a line that makes me think this may not be possible:
If the Future completes normally, its result is returned. If the Future completes with an error, the error and stack trace are wrapped in an AsyncError and thrown. If a microtask or message handler run during this call results in an unhandled exception, that exception will be propagated as though the microtask or message handler was the only Dart invocation on the stack. That is, unhandled exceptions in a microtask or message handler will skip over stacks suspended in a call to waitFor.
So I'm a little confused by the difference between a 'Future completes with an error' and 'a microtask ... results in an unhandled exception'.
The Future returned by your throwException will never complete with either a value or an error. The error thrown by the Future.delayed is an unhandled async error, it is unrelated entirely to the Future that is returned from that method. The ways to get a Future that completes with an error are:
The Future.error constructor.
Using Completer.completeError on a not yet completed Completer.
Using throw in an async method.
Using throw in a callback passed to a Future constructor, or .then.
So in your example, the Future.delayed creates a Future that will complete with an error because of the throw in the callback. Nothing is listening on this Future. There is no await, no .then or .catchError chained off of it. Once a Future completes with an error, and it has no handlers for that error, it will bubble up to the surrounding error zone. See https://dart.dev/articles/archive/zones#handling-asynchronous-errors
If you want to be able to react to unhandled errors you can use runZoned - getting the details right can be tricky. Note that it's possible to have multiple unhandled async errors resulting from running some bit of code, and that the completion of a Future does not necessarily mean that there aren't other unhandled async errors that can surface later.
From Nate Bosch I've devised a possible answer:
I hadn't realised that you can add multiple onCatchError methods to a future.
In DShell I'm passed the future so I had assumed I couldn't modify it.
So I added an onCatchError to the Future.delayed and then use the completer to pass the error back up the correct stack.
So this seems to work, I'm just uncertain if I need to actually implement a zone to cast my catch net a little further?
import 'dart:async';
import 'dart:cli';
void main() {
var future = throwExceptionV3();
try {
future
.catchError((Object e, StackTrace st) => print('onErrr: $e'))
.whenComplete(() => print('future completed'));
print(waitFor<int>(future));
} // on AsyncError
catch (e) {
if (e.error is Exception) {
print(e.error);
} else if (e is AsyncError) {
print('Rethrowing a non DShellException ${e}');
rethrow;
} else {
print('Rethrowing a non DShellException ${e}');
rethrow;
}
} finally {
print('waitForEx finally');
}
}
Future<int> throwExceptionV3() {
var complete = Completer<int>();
try
{
var future = Future.delayed(Duration(seconds: 2), () => throw Exception());
future.catchError((Object e) {
print('caught 1');
complete.completeError('caught ') ;
});
}
catch (e)
{
print ('e');
}
return complete.future;
}

using puppeteer-recorder to record video of browser

I'm trying to record puppeteer to see what happens when i run it on server, as I understand this package does what i want.
https://www.npmjs.com/package/puppeteer-recorder
so here is simplified version of my code
const puppeteer = require('puppeteer');
const { record } = require('puppeteer-recorder');
var path = 'C:\\wamp64\\www\\home_robot\\';
init_puppeteer();
const global_browser ;
async function init_puppeteer() {
global_browser = await puppeteer.launch({headless: false , args: ['--no-sandbox', '--disable-setuid-sandbox']});
check_login()
};
async function check_login()
{
try {
const page = await global_browser.newPage();
await page.setViewport({width: 1000, height: 1100});
await record({
browser: global_browser, // Optional: a puppeteer Browser instance,
page: page, // Optional: a puppeteer Page instance,
output: path + 'output.webm',
fps: 60,
frames: 60 * 5, // 5 seconds at 60 fps
prepare: function () {}, // <-- add this line
render: function () {} // <-- add this line
});
await page.goto('https://www.example.cob', {timeout: 60000})
.catch(function (error) {
throw new Error('TimeoutBrows');
});
await page.close();
}
catch (e) {
console.log(' LOGIN ERROR ---------------------');
console.log(e);
}
}
But I get this error
$ node home.js
(node:7376) UnhandledPromiseRejectionWarning: Error: spawn ffmpeg ENOENT
at Process.ChildProcess._handle.onexit (internal/child_process.js:240:19)
at onErrorNT (internal/child_process.js:415:16)
at process._tickCallback (internal/process/next_tick.js:63:19)
(node:7376) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This
error originated either by throwing inside of an async function without a catch
block, or by rejecting a promise which was not handled with .catch(). (rejection
id: 1)
(node:7376) [DEP0018] DeprecationWarning: Unhandled promise rejections are depre
cated. In the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.
LOGIN ERROR ---------------------
Error [ERR_STREAM_DESTROYED]: Cannot call write after a stream was destroyed
at doWrite (_stream_writable.js:406:19)
at writeOrBuffer (_stream_writable.js:394:5)
at Socket.Writable.write (_stream_writable.js:294:11)
at Promise (C:\wamp64\www\home_robot\node_modules\puppeteer-recorder\index.j
s:72:12)
at new Promise (<anonymous>)
at write (C:\wamp64\www\home_robot\node_modules\puppeteer-recorder\index.js:
71:3)
at module.exports.record (C:\wamp64\www\home_robot\node_modules\puppeteer-re
corder\index.js:44:11)
at process._tickCallback (internal/process/next_tick.js:68:7)
i've even ran npm i reinstall ffmpeg --with-libvpx
as it was suggested here
https://github.com/clipisode/puppeteer-recorder/issues/6
but still didnt work .... wha telse do i need to do ?
I know this would be a very late response to your question, but nevertheless.
A years ago, I visited this same stack-overflow thread and I had similar challenge of finding a screen recorder library which does a good job a capturing the video as well as offers an options to manually start and stop the recording.
Finally I wrote one for myself and distributed as NPM library...!!
https://www.npmjs.com/package/puppeteer-screen-recorder
Hope this is helpful...!!
Add two empty functions called prepare and render in the options.
await record({
browser: global_browser, // Optional: a puppeteer Browser instance,
page, // Optional: a puppeteer Page instance,
output: path + 'output.webm',
fps: 60,
frames: 60 * 5, // 5 seconds at 60 fps,
prepare: function () {}, // <-- add this line
render: function () {} // <-- add this line
});
Basically it's missing some default functions and the error is not handled properly.
Also, there's https://www.npmjs.com/package/puppeteer-capture which uses HeadlessExperimental protocol.

Nancy Exception in RequestStartup

I'm using Nancy to create a web api. I have a signed token that is passed in from the user to authenticate. This authentication is doen in the RequestStartup method in my own Bootstrapper. Now in some cases, for instance when I can't veryfy the signed token I would like to just be able to throw an exception and have that handled byt the OnError hanhdler in Nancy. However an exception thrown before the RequestStartup is finsihed isn't caught. The request generates a 500 error and I would like to return something else with my own error information.
I have the obvious case where I throw an exception but also possibilities of an exception being thrown in the GetIdentity() method.
I'm looking for any input in how to handle this.
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
base.RequestStartup(container, pipelines, context);
pipelines.OnError.AddItemToStartOfPipeline((ctx, exception) =>
container.Resolve<IErrorHandler>().HandleException(ctx, exception));
var identity = container.Resolve<IAuthenticationController>().GetIdentity();
var configuration = new StatelessAuthenticationConfiguration(_ => identity);
StatelessAuthentication.Enable(pipelines, configuration);
var logManager = new LogManager(context);
pipelines.AfterRequest.AddItemToEndOfPipeline(_ => logManager.Log());
try
{
X509Certificate2 clientCert = context.Request.ClientCertificate as X509Certificate2;
container.Resolve<ICertificateValidator>().Validate(clientCert);
}
catch (Exception ex)
{
throw new MklServerAuthenticationException(ErrorCodes.WrongOrNonexistingCertificate, ex);
}
}
Figured out a way to solve the above problem and thought somebody else might like to know. Replace the line in my code above, containing the GetIdentity() call, with the following:
Identity identity = null;
try
{
identity = container.Resolve<IAuthenticationController>().GetIdentity(requestInfo);
}
catch (Exception ex)
{
var exception = new MklAuthentcationException(ErrorCodes.TokenInvalid, ex);
context.Response = container.Resolve<IErrorHandler>().HandleException(context, exception);
pipelines.BeforeRequest.Invoke(context, CancellationToken.None);
}
I'm using the fact stated in nancy that:
The PreRequest hook is called prior to processing a request. If a hook returns a non-null response then processing is aborted and the response provided is returned.
So by setting a response (my error in this case) on the PreRequest hook and invoking it my error is returned and execution is stopped.
Maybe not the nicest solution... If you can figure out something better please let me know.

How catch (or know about) chrome.runtime.sendMessage Port Error?

When I try to send a message to another extension, occasionally I might have an invalid id (the extension may have been removed), but sendMessage does not ever notify me of this. As far as I can tell, it just prints to console.error:
This is miscellaneous_bindings Line 235 of Chrome's source code:
chromeHidden.Port.dispatchOnDisconnect = function( portId, errorMessage)
{
var port = ports[portId];
if (port) {
// Update the renderer's port bookkeeping, without notifying the browser.
CloseChannel(portId, false);
if (errorMessage) {
lastError.set(errorMessage, chrome);
//It prints: Port error: Could not establish connection. Receiving end does not exist.
console.error("Port error: " + errorMessage);
}
try {
port.onDisconnect.dispatch(port);
} finally {
port.destroy_();
lastError.clear(chrome);
}
}
};
As a result, my app is left trying over and over to send a message. The only hint I have is an empty response send back from sendResponse(), but any app can send an empty response object! How do I know it failed?
In the callback of sendResponse, look at the chrome.runtime.lastError property.
chrome.runtime.sendMessage("ID of extension", "message", function(response) {
var lastError = chrome.runtime.lastError;
if (lastError) {
console.log(lastError.message);
// 'Could not establish connection. Receiving end does not exist.'
return;
}
// Success, do something with response...
});