"Error: We're sorry, a server error occurred. Please wait a bit and try again." when getting screenshot of the webpage using Google Apps Script - google-apps-script

following my previous question, I wanted to get the screenshot of the webpage using Google Apps Script, thanks to Tanaike for the proposed script, it can get the webpage as required. However, after running the script for few times, I started to encounter the error saying: Error: We're sorry, a server error occurred. Please wait a bit and try again
After every run, I need to check the script whether it ran successfully or not, which is not suitable in my case. Following is the proposed script:
function getScreenShot() {
const url = "https://pagespeed.web.dev/report?url=http://www.cool.haus"; // This URL is from your question.
const blob = Charts
.newTableChart()
.setDataTable(Charts.newDataTable().addColumn(Charts.ColumnType.STRING, "").addRow([`<meta http-equiv="refresh" content="0; URL='${url}'">`]).build())
.setOption('allowHtml', true)
.setDimensions(1000, 2000)
.build()
.getBlob();
DriveApp.createFile(blob.setName("sample.png"));
}
This is the eror which I receive when running the function from script editor:
I found the following sources (link1 , link2) which also addresses the same issue, any help to solve this issue would be greatly appreciated. Thank you.

I could confirm your situation. In this case, how about retrying the request? When this is reflected in your script, it becomes as follows.
Modified script:
function getScreenShot() {
const url = "https://pagespeed.web.dev/report?url=http://www.cool.haus"; // This URL is from your question.
let blob;
let retry = 3;
let n = 1;
while (n <= retry && !blob) {
console.log(`Try: ${n}`);
try {
blob = Charts
.newTableChart()
.setDataTable(Charts.newDataTable().addColumn(Charts.ColumnType.STRING, "").addRow([`<meta http-equiv="refresh" content="0; URL='${url}'">`]).build())
.setOption('allowHtml', true)
.setDimensions(1000, 2000)
.build()
.getBlob();
} catch ({ message }) {
console.log(message);
Utilities.sleep(5000); // This might not be required to be used.
}
n++;
}
if (!blob) {
console.log("Image blob couldn't be retrieved.");
return;
}
DriveApp.createFile(blob.setName("sample.png"));
}
When this script is run, when an error occurs, the script is retied. When the image blob is retrieved, the retry is finished. In this case, the sample number of retries is 3. And, in this sample script, you can see the process in the log.

Related

Google Sheets App Script: Internal error executing the custom function

I have a custom function that periodically will return the error: Internal error executing the custom function. The custom function execution lasts 0 (zero) seconds according to the executions page on the App Script website.
I have contact Google multiple times about this insisting that it is a platform error, but I get the same answer every time - "Add a random delay in your function" or "Use exponential backoff". I have tried adding a random delay and this did help slightly, but the error was still there - just less occurrences. I currently have exponential backoff implemented for all App Script APIs and even with this retry logic I get the same error.
I've followed the entirety of the App Script documentation for best practices including using ranges, and even with this the error is still there.
Is anyone else experiencing this? I tried adding a Logger.log on the very first line of my custom function and this code does not trigger when I receive this error. This leads me to believe that the custom function calls just never reach the server where the code executes. This is why I believe it's a platform error.
This is the exponential backoff code:
function call_(func) {
for (var n = 0; n < 6; n++) {
try {
return func()
} catch (e) {
Logger.log(`Retrying... ${n + 1} times exception: ${e}`)
if (n === 6 - 1) {
throw e
}
Utilities.sleep(
Math.pow(2, n) * 1000 + Math.round(Math.random() * 1000)
)
}
}
}
This is a condensed version of the custom function:
function CUSTOMFUNCTION() {
let apiResponse = call_(() =>
UrlFetchApp.fetch(someUrl, {
muteHttpExceptions: true,
})
)
let response = JSON.parse(call_(() => apiResponse.getContentText()))
// do some logic with the response
return value
}
I'm aware that http exception won't be caught and this is intentional. While testing the custom function in the development sheet that the script is attached to, I can't reproduce the error no matter what I try. The error only ever happens after the script is deployed via Google Cloud Platform (GCP). For clarity, inside GCP I use the Google Workspace Marketplace SDK to attach the script ID in order to make the add-on available to anyone to add to their Google Sheets.
If anyone has any input to help with resolving this error I would appreciate it as the Google team hasn't been very helpful over the past few months of triaging this issue.
API Limits.
https://developers.google.com/sheets/api/limits
implement exponential backoff, solved the issue for us.
async function myFunction(params) {
let result = -1
for (let i = 0; i<10; i++){
try {
let response = await UrlFetchApp.fetch("MY URL");
result = await response.getContentText()
if (result >= 0) {return result}
} catch(err) {
await setTimeout(r, 1000*i);
}
}
return err.message
}
Then we also set the sheet to auto-refresh formulas
https://spreadsheetpoint.com/auto-refresh-google-sheets/
(might not be related)
The issue ended up being caused by our overall script size.
After reaching out to google we were informed that on each custom function request Google will retrieve the entire contents of your App Script code. Not just the code/file used for custom functions. The upper limit on their download size is 5mb.
Because of this, we changed our webpack configuration resulting in a much smaller bundle size (<1mb). Google also created a file cache to speed up this process on larger App Script projects.
Hopefully this'll help someone else who runs into this issue.

Google Apps Script: What is the correct way to check if I have access to a Google Spreadsheet by URL

I am currently writing a Google Apps Script inside a Google Sheet to read data from a list of spreadsheets (spreadsheet url is provided by the user). However, I cant seems to find a way to check if the url is valid or if user have access to the spreadsheet or not before calling SpreadsheetApp.openByUrl().
I have written the following code to "validate" the url:
for(int i = 0; i < urls.length; i++) {
let spreadsheet = null
try {
spreadsheet = SpreadsheetApp.openByUrl(urls[i]);
} catch (e) {
continue;
}
// Continue to do other stuff to read data from spreadsheet...
}
This however has an issue, it was able to catch the first few 'You do not have permission to access the requested document.' exception. But after a certain number of exception had occur, I would get a permenant error that cant be caught, stopping the script all together.
Is there a better way to do this?
Minimal reproducible example:
Create 3 google sheet using different google account
Using a different google account, create a google sheet and add the following code into Code.gs
function myFunction() {
// Put any 3 real spreadsheet url that you do not have access to
let urls = [
"https://docs.google.com/spreadsheets/d/1gOyEAz0amm4RghpE4B7f26okU3PG3vWZkrfiC-SBlbw/edit#gid=0",
"https://docs.google.com/spreadsheets/d/1Oia7ADu5BmYroUq1SLyDMHTJowrwSXOhCEyNO3nXmMA/edit#gid=0",
"https://docs.google.com/spreadsheets/d/1HE_IXURpBr_FJN--mwLo6k9gih07ZEtDGBqYSk6KgiA/edit#gid=0",
]
urls.forEach(url => {
try {
SpreadsheetApp.openByUrl(url)
} catch (e) {
console.log("Unable to open this spreadsheet")
}
})
}
function onOpen() {
SpreadsheetApp.getUi().createMenu("Test").addItem("myFunction", "myFunction").addToUi()
}
Run the function once in the apps script panel and authorize the application
Refresh this google sheet
Wait for the Custom Menus to show up and press "Menu" > "myFunction"
As you can see, the openByUrl() call is sitting inside the try catch block, however when you run the function through custom menu, you will still get "Error: You do not have permission to access the requested document.".
Executions Log:
From your question, I thought that your situation might be due to the specification or a bug of SpreadsheetApp.openByUrl. If my understanding is correct, in order to avoid this issue, how about putting the method for checking whether the file can be used before SpreadsheetApp? In your script, how about the following modification?
From :
SpreadsheetApp.openByUrl(url)
To:
var fileId = url.split("/")[5];
var file = DriveApp.getFileById(fileId);
spreadsheet = SpreadsheetApp.open(file);
In this modification, the file is retrieved with DriveApp.getFileById(fileId). When fileId cannot be used, an error occurs. But in this case, try-catch can be correctly worked. By this, the issue of SpreadsheetApp doesn't occur.

Google apps script URL changes and does not open

I have an issue where other uses of my Google apps script's url is getting changed. Due to this issue they are not able to open the html page.
Original url "https://script.google.com/a/macros/google.com/s/abcxyz-kaskasdb/exec?v=applyleave"
changed url "https://script.google.com/macros/s/abcxyz-kaskasdb/exec?v=applyleave"
I realize "/a" and "/google.com" is getting removed some how.
How can I fix this issue.
Here is my code that is rendered:-
function include(filename)
{
return HtmlService.createHtmlOutputFromFile(filename).getContent();
}
function render(file,argsObject){
var tmp = HtmlService.createTemplateFromFile(file);
if(argsObject){
var keys = Object.keys(argsObject);
keys.forEach(function(key){
tmp[key] = argsObject[key];
});
}
return tmp.evaluate();
}
And here's the code for the server which should accept POST requests:
var Route = {};
Route.path = function (route,callback){
Route[route] = callback;
}
function doGet(e) {
Route.path("applyleave",leaveApply)
Route.path("leaveroster",leave_Roster)
if (Route[e.parameters.v]){
return Route[e.parameters.v]();}
else {
html = HtmlService.createTemplateFromFile('home');
return html.evaluate();
}
}
The error received from other side is this :-
Can anyone explain and provide solution?
There is nothing wrong with the deployment URL getting changed - it is common for Google to perform this redirection.
The issue is can be rather a permission issue. Make sure you deploy the WebApp as "Anyone, even anonymous".
However, currently I am experiencing the same behavior like you due to a multiply reported recent bug:
https://issuetracker.google.com/72798634
https://issuetracker.google.com/165350842
https://issuetracker.google.com/166010550
https://issuetracker.google.com/166320373
https://issuetracker.google.com/167692852
https://issuetracker.google.com/169349069
Please refer to this Github repo.
After the first deployment, you need to make further deployments on the same version by clicking on "Manage Deployment" and then selecting the version to "New version"
enter image description here

"Service Spreadsheets failed" error when calling openById method in a standalone script project

I've been trying to access spreadsheets from a standalone google script, but keep getting the following error message:
Exception: Service Spreadsheets failed while accessing document with id abcdefg_example_id. (Code:25:33)
function doSomething() {
var theSheet = SpreadsheetApp.openById("abcdefg_example_id");
if(theSheet == null){
Logger.log("NO SPREADSHEET");
} else {
Logger.log("SPREADSHEET REEE");
}
Logger.log(theSheet.getName());
}
How do I access a spreadsheet by ID from a standalone google script?
Also, I've tried running the same code within a container bound script and it won't work when trying to open a different spreadsheet. Same error message. My goal is basically to access and modify other spreadsheets ideally from a standalone script but I don't mind if it has to be done another way.
I tried the same code on my PC using personal files and it works. I'm trying to get this code to work on a work computer but it runs into the issue above. I'm guessing maybe it's an authorization problem or perhaps an administrator restriction of some sort. Any ideas?
Have you checked if the spreadsheet you're trying to "openById" is in a Google Doc format(has been converted when uploaded to Gdrive)? if it is not, google app script will throw this error.
I just did this:
function getDataFromAnotherSpreadsheet() {
const ss=SpreadsheetApp.openById("ssid");
const sh=ss.getSheetByName('Sheet1');
const rg=sh.getDataRange();
const vs=rg.getValues();
let html="";
vs.forEach(function(r,i){
html+=r.join(',') + '<br />';
});
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutput(html), "Results")
}
My Output:
COL12,A1,COL10,B1,COL8,C1,COL6,D1,COL4,E1,COL2
133,1,109,1,85,1,61,1,37,1,13
134,2,110,2,86,2,62,2,38,2,14
135,3,111,3,87,3,63,3,39,3,15
136,4,112,4,88,4,64,4,40,4,16
137,5,113,5,89,5,65,5,41,5,17
138,6,114,6,90,6,66,6,42,6,18
139,7,115,7,91,7,67,7,43,7,19
140,8,116,8,92,8,68,8,44,8,20
141,9,117,9,93,9,69,9,45,9,21
.....
Getting ID: (animation)

removeEditor failing on some editors

I have some google apps script code that adds Editors to a document with no problem. However, when I run the following code I find that some of the editors are removed, and some error with the following message: "Exception: We're sorry, a server error occurred. Please wait a bit and try again."
var file = DocsList.getFileById( fid );
var editors = file.getEditors();
for ( el = 0; el < editors.length ; el++ ) {
file.removeEditor( editors[ el ] );
}
Given that the editors are retrieved from the file itself, and then fail to be removed, I cannot see how to progress this, as the error message offers no help.
Has anyone experienced similar. I cannot see any issues raised against this.
Thanks in advance.
Chris
You could try to put your file.remove(editors[ el ]) in a try-catch structure to catch the error (and see it) and let your app finish in every case.
Example :
function myFunction() {
var file = DocsList.getFileById( '0AnqS........bW1DNnVBbVE' );
var editors = file.getEditors();
Logger.log(editors.join())
for ( el = 0; el < editors.length ; el++ ) {
try{
file.removeEditor( editors[ el ] );
Logger.log(editors[el]+' removed');// this editor is successfully removed
} catch(error)
{Logger.log('error on el = '+editors[el]+' = '+error)
}
}
}
This of course brings no answer on your issue but it could help to see what is happening in more details, which editor gets an error and which doesn't.
I have the same experience, both removeEditor and removeViewer can fail for some users, despite obtaining the user object from the getViewers or getEditors methods.
My observation is that unless the returned user object is a Google apps account holder then the remove action will fail.
So, on a google apps domain example.com a file might be shared with the following email addresses:
normal_account#example.com
group_account#example.com
third_party#some_other_example.com
removeEditor/removeViewer will only succeed for the first address on the list.
Although the call to getEditors/getViewers returns a list of user objects, some of the objects appear empty eg user.getEmail() returns a blank string, which is what the API documentation says will happen https://developers.google.com/apps-script/reference/drive/user#getEmail%28%29 if the email address is not available.