I have a NodeJS app that among doing other things sends emails using the Gmail api with simple pdf attachments. This worked for months and then suddenly it stopped working and I started getting 500 code errors (below). I had changed neither the code nor the account details. Furthermore, it works perfectly fine without an attachment, and the exact same code works perfectly fine with another email address with or without attachments. I have some excerpts of code below that I have tried running.
Error:
throw new common_1.GaxiosError(`Request failed with status code ${translatedResponse.status}`, opts, translatedResponse);
^
GaxiosError: Internal error encountered.
at Gaxios._request (/Users/baileytuckman/Desktop/Code/GmailTest/node_modules/gaxios/build/src/gaxios.js:142:23)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async OAuth2Client.requestAsync (/Users/baileytuckman/Desktop/Code/GmailTest/node_modules/google-auth-library/build/src/auth/oauth2client.js:429:18)
at async exports.sendMail (/Users/baileytuckman/Desktop/Code/GmailTest/main.js:25:15) {
config: {
url: 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send',
method: 'POST',
apiVersion: '',
userAgentDirectives: [
{
product: 'google-api-nodejs-client',
version: '7.2.0',
comment: 'gzip'
}
],
paramsSerializer: [Function (anonymous)],
data: {
raw: 'Q29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0DQpDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50OyBmaWxlbmFtZT10ZXh0Mi50eHQNClgtQXBwbGljYXRpb24tRGV2ZWxvcGVyOiBCYWlsZXkgVHVja21hbg0KWC1BcHBsaWNhdGlvbi1WZXJzaW9uOiB2MS4wLjANClRvOiBtZUBleGFtcGxlLmNvbQ0KUmVwbHktVG86IG1lQGV4YW1wbGUuY29tDQpTdWJqZWN0OiBUZXN0DQpNZXNzYWdlLUlEOiA8ZTY1YWY4Y2ItYzFkMS1iNTdkLTM3ZTctYWFmNjNhZTk3Yjk5QGV4YW1wbGUuY29tPg0KRGF0ZTogVGh1LCAwNiBGZWIgMjAyNSAxODo0Njo0NiArMDAwMA0KTUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgbmFtZT10ZXh0Mi50eHQNCg0KWVVkV2MySkhPR2RrTWpsNVlrZFJhQT09DQo'
},
headers: {
'x-goog-api-client': 'gdcl/7.2.0 gl-node/20.12.0',
'Accept-Encoding': 'gzip',
'User-Agent': 'google-api-nodejs-client/7.2.0 (gzip)',
Authorization: '<<REDACTED> - See `errorRedactor` option in `gaxios` for configuration>.',
'Content-Type': 'application/json'
},
params: {},
validateStatus: [Function (anonymous)],
retry: true,
body: '{"raw":"Q29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0DQpDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50OyBmaWxlbmFtZT10ZXh0Mi50eHQNClgtQXBwbGljYXRpb24tRGV2ZWxvcGVyOiBCYWlsZXkgVHVja21hbg0KWC1BcHBsaWNhdGlvbi1WZXJzaW9uOiB2MS4wLjANClRvOiBtZUBleGFtcGxlLmNvbQ0KUmVwbHktVG86IG1lQGV4YW1wbGUuY29tDQpTdWJqZWN0OiBUZXN0DQpNZXNzYWdlLUlEOiA8ZTY1YWY4Y2ItYzFkMS1iNTdkLTM3ZTctYWFmNjNhZTk3Yjk5QGV4YW1wbGUuY29tPg0KRGF0ZTogVGh1LCAwNiBGZWIgMjAyNSAxODo0Njo0NiArMDAwMA0KTUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgbmFtZT10ZXh0Mi50eHQNCg0KWVVkV2MySkhPR2RrTWpsNVlrZFJhQT09DQo"}',
responseType: 'unknown',
errorRedactor: [Function: defaultErrorRedactor],
retryConfig: {
currentRetryAttempt: 0,
retry: 3,
httpMethodsToRetry: [ 'GET', 'HEAD', 'PUT', 'OPTIONS', 'DELETE' ],
noResponseRetries: 2,
retryDelayMultiplier: 2,
timeOfFirstRequest: 1738867607149,
totalTimeout: 9007199254740991,
maxRetryDelay: 9007199254740991,
statusCodesToRetry: [ [ 100, 199 ], [ 408, 408 ], [ 429, 429 ], [ 500, 599 ] ]
}
},
response: {
config: {
url: 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send',
method: 'POST',
apiVersion: '',
userAgentDirectives: [
{
product: 'google-api-nodejs-client',
version: '7.2.0',
comment: 'gzip'
}
],
paramsSerializer: [Function (anonymous)],
data: {
raw: 'Q29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0DQpDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50OyBmaWxlbmFtZT10ZXh0Mi50eHQNClgtQXBwbGljYXRpb24tRGV2ZWxvcGVyOiBCYWlsZXkgVHVja21hbg0KWC1BcHBsaWNhdGlvbi1WZXJzaW9uOiB2MS4wLjANClRvOiBtZUBleGFtcGxlLmNvbQ0KUmVwbHktVG86IG1lQGV4YW1wbGUuY29tDQpTdWJqZWN0OiBUZXN0DQpNZXNzYWdlLUlEOiA8ZTY1YWY4Y2ItYzFkMS1iNTdkLTM3ZTctYWFmNjNhZTk3Yjk5QGV4YW1wbGUuY29tPg0KRGF0ZTogVGh1LCAwNiBGZWIgMjAyNSAxODo0Njo0NiArMDAwMA0KTUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgbmFtZT10ZXh0Mi50eHQNCg0KWVVkV2MySkhPR2RrTWpsNVlrZFJhQT09DQo'
},
headers: {
'x-goog-api-client': 'gdcl/7.2.0 gl-node/20.12.0',
'Accept-Encoding': 'gzip',
'User-Agent': 'google-api-nodejs-client/7.2.0 (gzip)',
Authorization: '<<REDACTED> - See `errorRedactor` option in `gaxios` for configuration>.',
'Content-Type': 'application/json'
},
params: {},
validateStatus: [Function (anonymous)],
retry: true,
body: '{"raw":"Q29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0DQpDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50OyBmaWxlbmFtZT10ZXh0Mi50eHQNClgtQXBwbGljYXRpb24tRGV2ZWxvcGVyOiBCYWlsZXkgVHVja21hbg0KWC1BcHBsaWNhdGlvbi1WZXJzaW9uOiB2MS4wLjANClRvOiBtZUBleGFtcGxlLmNvbQ0KUmVwbHktVG86IG1lQGV4YW1wbGUuY29tDQpTdWJqZWN0OiBUZXN0DQpNZXNzYWdlLUlEOiA8ZTY1YWY4Y2ItYzFkMS1iNTdkLTM3ZTctYWFmNjNhZTk3Yjk5QGV4YW1wbGUuY29tPg0KRGF0ZTogVGh1LCAwNiBGZWIgMjAyNSAxODo0Njo0NiArMDAwMA0KTUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogdGV4dC9wbGFpbjsgbmFtZT10ZXh0Mi50eHQNCg0KWVVkV2MySkhPR2RrTWpsNVlrZFJhQT09DQo"}',
responseType: 'unknown',
errorRedactor: [Function: defaultErrorRedactor]
},
data: {
error: {
code: 500,
message: 'Internal error encountered.',
errors: [
{
message: 'Internal error encountered.',
domain: 'global',
reason: 'backendError'
}
],
status: 'INTERNAL'
}
},
headers: {
'alt-svc': 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000',
'content-encoding': 'gzip',
'content-type': 'application/json; charset=UTF-8',
date: 'Thu, 06 Feb 2025 18:46:47 GMT',
server: 'ESF',
'transfer-encoding': 'chunked',
vary: 'Origin, X-Origin, Referer',
'x-content-type-options': 'nosniff',
'x-frame-options': 'SAMEORIGIN',
'x-xss-protection': '0'
},
status: 500,
statusText: 'Internal Server Error',
request: {
responseURL: 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send'
}
},
error: undefined,
status: 500,
code: 500,
errors: [
{
message: 'Internal error encountered.',
domain: 'global',
reason: 'backendError'
}
],
[Symbol(gaxios-gaxios-error)]: '6.7.1'
}
Node.js v20.12.0
This file generates an auth token:
// TODO Run this to generate credentials for google apis
// To reset credentials delete token.json and run again
const fs = require("fs").promises;
const path = require("path");
// const process = require("process");
const { authenticate } = require("@google-cloud/local-auth");
const { google } = require("googleapis");
// If modifying these scopes, delete token.json.
const SCOPES = ["https://www.googleapis.com/auth/calendar.events","https://www.googleapis.com/auth/gmail.send", "https://www.googleapis.com/auth/drive.readonly"];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = path.join(process.cwd(), "./token.json");
const CREDENTIALS_PATH = path.join(process.cwd(), "./OAuthClient.json");
/**
* Reads previously authorized credentials from the save file.
*
* @return {Promise<OAuth2Client|null>}
*/
async function loadSavedCredentialsIfExist() {
try {
const content = await fs.readFile(TOKEN_PATH);
const credentials = JSON.parse(content);
return google.auth.fromJSON(credentials);
} catch (err) {
return null;
}
}
/**
* Serializes credentials to a file compatible with GoogleAuth.fromJSON.
*
* @param {OAuth2Client} client
* @return {Promise<void>}
*/
async function saveCredentials(client) {
const content = await fs.readFile(CREDENTIALS_PATH);
const keys = JSON.parse(content);
const key = keys.installed || keys.web;
const payload = JSON.stringify({
type: "authorized_user",
client_id: key.client_id,
client_secret: key.client_secret,
refresh_token: client.credentials.refresh_token,
});
await fs.writeFile(TOKEN_PATH, payload);
}
/**
* Load or request or authorization to call APIs.
*/
async function authorize() {
let client = await loadSavedCredentialsIfExist();
if (client) {
return client;
}
client = await authenticate({
scopes: SCOPES,
keyfilePath: CREDENTIALS_PATH,
});
if (client.credentials) {
await saveCredentials(client);
}
return client;
}
authorize();
This file has the functionality for sending:
const { google } = require("googleapis");
const MailComposer = require("nodemailer/lib/mail-composer");
const credentials = require("./OAuthClient.json");
const tokens = require("./token.json");
const getGmailService = () => {
const { client_secret, client_id, redirect_uris } = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
oAuth2Client.setCredentials(tokens);
const gmail = google.gmail({ version: "v1", auth: oAuth2Client });
return gmail;
};
const createMail = async (options) => {
const mailComposer = new MailComposer(options);
const message = await mailComposer.compile().build();
return Buffer.from(message).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
};
exports.sendMail = async (options) => {
const gmail = getGmailService();
const rawMessage = await createMail(options);
const data = await gmail.users.messages.send({
userId: "me",
resource: {
raw: rawMessage,
},
});
console.log(data);
return data;
};
exports.emailOptions = {
to: "",
cc: "",
replyTo: "me@example.com",
subject: "",
text: "",
html: "",
// attachments: fileAttachments,
headers: [
{ key: "X-Application-Developer", value: "Bailey Tuckman" },
{ key: "X-Application-Version", value: "v1.0.0" },
],
};
and finally to send:
const { sendMail, emailOptions } = require("./main");
let messageOptions = emailOptions;
messageOptions.to = "me@example.com";
messageOptions.subject = `Test`;
messageOptions.textEncoding = "base64"
messageOptions.attachments = {
filename: "text2.txt",
content: "aGVsbG8gd29ybGQh",
encoding: "utf8",
};
let messageId = sendMail(messageOptions).then((res) => {
console.log(res);
});
As I mentioned, with the original account it does not work, but if I comment out the attachment then it does. Sending from a different email account it works either way. I have also tried creating a new client Id which as expected didn't help.
I am not sure why this is happening, but I appreciate any help.
As per Resolve a 500 error: Backend error:
A
backendError
occurs when an unexpected error arises while processing the request.To fix this error, retry failed requests.
To Retry failed requests to resolve errors:
You can periodically retry a failed request over an increasing amount of time to handle errors related to rate limits, network volume, or response time. For example, you might retry a failed request after one second, then after two seconds, and then after four seconds. This method is called exponential backoff and it is used to improve bandwidth usage and maximize throughput of requests in concurrent environments.
Start retry periods at least one second after the error.
@Phil's comment is correct that the error
is on the
side of Google
. If you already did what was previously mentioned and it's still not working, it's time to reach out to their support channels
.
I recommend that you submit a bug report to let Google know about the unusual behavior that the code does not work
on the original account
when there's an attachment but works perfectly fine without an attachment
and works perfectly fine with another email address with or without attachments
since I haven't found a report when I searched the Google Issue Tracker.
You may Find support for the Gmail API directly on Developer product feedback
and through Contact Google Workspace support
.