Netlify forms -> submission-created failing in production

I have a contact form connected to a serverless function netlify/functions/submission-created

<!-- netlify/functions/submission-created.js -->

const sgMail = require("@sendgrid/mail");
const process = require("process");


exports.handler = async (event) => {
  const payload = JSON.parse(event.body).payload;
  const data =;

  let timestamp = payload.created_at;
  let deliveryDate = new Date(timestamp);

  const msgToHost = {
    to: process.env.NETLIFY_EMAILS_SENDER,
    from: `... <${process.env.NETLIFY_EMAILS_SENDER}>`,
    subject: `New enquiry received from ${data.firstName} ${data.lastName}`,
    html: "More information...",

    .then((response) => {
    .catch((error) => {

  return {
    statusCode: 200,
    body: JSON.stringify("Email sent successfully"),

This function is triggered when the following form is submitted

<!-- _includes/forms/contact.njk -->

  <div class="row gy-4">
      <i class="bi bi-envelope" style="font-size:38px;color:#2e004f;"></i>
      <span style="margin-left:1rem; font-size:1.5rem;color:#00046d;font-weight:700;">Send us a message now</span>
    <p style="display:none;">
        Don’t fill this out if you’re human: <input name="bot-field" />
      <input type="hidden" name="form-name" value="contact"/>
      <input type="hidden" name="template-id" value="enter-template-id-here"/>
    <div class="col-md-6">
      <input type="text" id="firstName" name="firstName" class="form-control" placeholder="Your  First Name" required>
    <div class="col-md-6">
      <input type="text" id="lastName" name="lastName" class="form-control" placeholder="Your Last Name" required>

    <div class="col-md-12 ">
      <input type="email" id="email" name="email" class="form-control" placeholder="Your Email" required>

    <div class="col-md-12">
      <textarea class="form-control" id="message" name="message" rows="6" placeholder="Message"></textarea>
    <div class="col-md-12">
      <div data-netlify-recaptcha="true"></div>
    <div class="col-md-12 text-center">
      <button type="submit">Send Message</button>



const processForm = form => {
  const data = new FormData(form)
  const firstName = document.getElementById('firstName').value
  const email = document.getElementById('email').value

  fetch('/', {
    body: data,
    form.innerHTML = `
    <div class="info-box" data-aos="fade-up">
      <i class="bi bi-envelope-check"></i>
      <h3>Thank you ${firstName} for your message</h3>
      <p>We will respond as quickly as possible.</p>
      <p>We have also sent a copy of your message to ${email}</p>
    form.innerHTML = `<div class="form--error">Error: ${error}</div>`;
const emailForm = document.querySelector('.email-form')
if (emailForm) {
  emailForm.addEventListener('submit', e => {

This all works without error when tested in localhost dev, but once deployed to netlify the function fails.
The contact form refreshes and displays the success message, the fetch shows status 200 in the network tab, but there is no response object returned and there is no email delivered.

the function log:

Jun 28, 04:25:15 PM: 1572a0b0 ERROR  Error: connect ETIMEDOUT
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1278:16) {
  errno: -110,
  code: 'ETIMEDOUT',
  syscall: 'connect',
  address: 'x.x.x.x',
  port: 443,
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    adapter: [Function: httpAdapter],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: Infinity,
    maxBodyLength: Infinity,
    validateStatus: [Function: validateStatus],
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
      'User-Agent': 'sendgrid/7.7.0;nodejs',
      Authorization: 'Bearer SG.xxxxx',
      'Content-Length': 597
    url: '/v3/mail/send',
    method: 'post',
    data: `{"from":{"email":"a@b.c","name":"X"},"subject":"New enquiry received","personalizations":[{"to":[{"email":"a@b.c"}]}],"content":[{"value":"\\n      <div>\\n        <p>\\n          A new enquiry fromx has been received.\\n        </p>\\n        <blockquote></blockquote>\\n        <p>x'semail address is v@tz.m</p>\\n        <p>&nbsp;</p>\\n        <p>&nbsp;</p>\\n        <p>Delivered: 6/28/2023, 4:22:08 AM</p>\\n      </div>\\n    ","type":"text/html"}],"reply_to":{"email":"a@x.c"}}`,
    baseURL: ''
  request: <ref *1> Writable {
    _writableState: WritableState {
      objectMode: false,
      highWaterMark: 16384,
      finalCalled: false,
      needDrain: false,
      ending: false,
      ended: false,
      finished: false,
      destroyed: false,
      decodeStrings: true,
      defaultEncoding: 'utf8',
      length: 0,
      writing: false,
      corked: 0,
      sync: true,
      bufferProcessing: false,
      onwrite: [Function: bound onwrite],
      writecb: null,
      writelen: 0,
      afterWriteTickInfo: null,
      buffered: [],
      bufferedIndex: 0,
      allBuffers: true,
      allNoop: true,
      pendingcb: 0,
      constructed: true,
      prefinished: false,
      errorEmitted: false,
      emitClose: true,
      autoDestroy: true,
      errored: null,
      closed: false,
      closeEmitted: false,
      [Symbol(kOnFinished)]: []
    _events: [Object: null prototype] {
      response: [Function: handleResponse],
      error: [Function: handleRequestError],
      socket: [Function: handleRequestSocket]
    _eventsCount: 3,
    _maxListeners: undefined,
    _options: {
      maxRedirects: 21,
      maxBodyLength: Infinity,
      protocol: 'https:',
      path: '/v3/mail/send',
      method: 'POST',
      headers: [Object],
      agent: undefined,
      agents: [Object],
      auth: undefined,
      hostname: '',
      port: null,
      nativeProtocols: [Object],
      pathname: '/v3/mail/send'
    _ended: false,
    _ending: true,
    _redirectCount: 0,
    _redirects: [],
    _requestBodyLength: 597,
    _requestBodyBuffers: [ [Object] ],
    _onNativeResponse: [Function (anonymous)],
    _currentRequest: ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: 597,
      _hasBody: true,
      _trailer: '',
      finished: false,
      _headerSent: true,
      _closed: false,
      socket: [TLSSocket],
      _header: 'POST /v3/mail/send HTTP/1.1\r\n' +
        'Accept: application/json\r\n' +
        'Content-Type: application/json\r\n' +
        'User-Agent: sendgrid/7.7.0;nodejs\r\n' +
        'Authorization: Bearer SG.xxxxx\r\n' +
        'Content-Length: 597\r\n' +
        'Host:\r\n' +
        'Connection: close\r\n' +
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      path: '/v3/mail/send',
      _ended: false,
      res: null,
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: '',
      protocol: 'https:',
      _redirectable: [Circular *1],
      [Symbol(kCapture)]: false,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(kEndCalled)]: false,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype],
      [Symbol(kUniqueHeaders)]: null
    _currentUrl: '',
    [Symbol(kCapture)]: false
  response: undefined,
  isAxiosError: true,
  toJSON: [Function: toJSON]

It appears that for some reason sendgrid is rejecting the request when it comes from my live site.

Any help appreciated.


Hiya :wave:t6:, can you post a link to your live form? Also if you haven’t already give this support guide a read as it has loads of details about forms debugging.

This Support Guide is the first port of call to debug any forms issues. Please start here and work through these resources!

We also recommend trying to search the forums or look at topics tagged Netlify forms if you haven’t already - it’s likely your question was already asked by someone else!

Hi Sam,
Thanks for your response. I did search the forums for similar issues and found two threads, both mentioned DNS as a possible reason but neither helped me. I have also been all over the doc’s.

The fact the issue only appears in the production site does point to a configuration or DNS issue. The custom domain in question is manged by netlify.

The live/production/deployed html form is Contact Us
form name is ‘contact’ no quotes.
I have supplied the logs I have available

I have tried removing the custom domain and serving the site through, same result.

Hope you can help. Given the fact that I tout my business as ‘connecting the web’ it’s a pretty poor look when I can’t get my own contact form working…

Martyn C

If I recall, some users reported, they needed some additional configuration on SendGrid side - some trusted senders or something like that. You can try contacting them to be sure.