Intermittent error from Edge function

I am sometimes getting the following error page on my site

The edge function in question is as follows (it controls whether a cookie banner is shown/what options are selected on a manage cookies page) and runs on every page

// Placeholders in cookies template to replace with dynamic content
const placeholders = {
  cookieBanner: /---- EDGE_FUNCTION_COOKIE_BANNER ----/i,
  analytical: {
    false: /---- EDGE_FUNCTION_COOKIE_ANALYTICAL_OFF ----/i,
    true: /---- EDGE_FUNCTION_COOKIE_ANALYTICAL_ON ----/i,
  },
  advertisment: {
    false: /---- EDGE_FUNCTION_COOKIE_ADVERTISMENT_OFF ----/i,
    true: /---- EDGE_FUNCTION_COOKIE_ADVERTISMENT_ON ----/i,
  },
  functional: {
    false: /---- EDGE_FUNCTION_COOKIE_FUNCTIONAL_OFF ----/i,
    true: /---- EDGE_FUNCTION_COOKIE_FUNCTIONAL_ON ----/i,
  },
};

/**
 * Fallback if function fails (remove placeholders)
 *
 * @param {String} page - Original page
 *
 * @returns {String} New page markup
 */
const handleFail = async (page) => {
  for (const placeholder in placeholders) {
    if (placeholder !== "cookieBanner") {
      for (const child in placeholders[placeholder]) {
        page = page.replace(placeholders[placeholder][child], "");
      }
    } else {
      page = page.replace(placeholders[placeholder], "");
    }
  }
  return page;
};

export default async (request, context) => {
  // Don't run this function on asset files
  if (request?.url?.indexOf("/assets/") > -1) return;

  // Get the page content
  const response = await context.next();
  const page = await response.text();

  try {
    // Set up new variable to page with replaced content
    let newPage = page;

    // Get users current cookie policy settings
    const policyValue = context.cookies.get("bv-cookie-policy")
      ? JSON.parse(context.cookies.get("bv-cookie-policy"))
      : null;

    // Snippets to replace placeholders with (within function as these are amending depending on the users policy)
    const templates = {
      cookieBanner: `
      <div id="cookie-banner" class="pt-200 pb-200 xl:pt-600 xl:pb-600 bg-supplementary-light">
        <div class="container container--thin">
          <h2 class="heading-3 mb-550">Let us know you agree to cookies</h2>
          <p class="mb-600">We use some essential cookies to make this website work.<br>We'd like to set additional cookies to give you the best online experience.</p>
          <div class="mb-200"><button type="button" data-button="accept-cookies">Accept all cookies</button></div>
          <div><a href="/manage-cookies" class="btn">Manage cookies</a></div>
        </div>
      </div>
      `,
      analytical: {
        false: `<input checked type="radio" name="analytical" id="analytical-false" value="false">`,
        true: `<input type="radio" name="analytical" id="analytical-true" value="true">`,
      },
      advertisment: {
        false: `<input checked type="radio" name="advertisment" id="advertisment-false" value="false">`,
        true: `<input type="radio" name="advertisment" id="advertisment-true" value="true">`,
      },
      functional: {
        false: `<input checked type="radio" name="functional" id="functional-false" value="false">`,
        true: `<input type="radio" name="functional" id="functional-true" value="true">`,
      },
    };

    // Loop through placeholder
    for (const placeholder in placeholders) {
      // Show cookie banner in user has no policy
      if (placeholder === "cookieBanner") {
        const bannerMarkup = !policyValue ? templates[placeholder] : "";
        newPage = newPage.replace(placeholders[placeholder], bannerMarkup);
      }

      // Replace placeholders with radio fields
      if (placeholder !== "cookieBanner") {
        for (const child in placeholders[placeholder]) {
          // If user has a cookie policy, remove all checked attributes then add checked attribute to relevent radio input templates
          if (policyValue) {
            templates[placeholder][child] = templates[placeholder][
              child
            ].replace("<input checked", "<input");

            const selectedPolicyValue = policyValue[placeholder];
            const policyTypeTemplates = templates[placeholder];

            policyTypeTemplates[selectedPolicyValue] = policyTypeTemplates[
              selectedPolicyValue
            ].replace("<input", "<input checked");
          }

          newPage = newPage.replace(
            placeholders[placeholder][child],
            templates[placeholder][child]
          );
        }
      }
    }

    // Return new page
    return new Response(newPage, response);
  } catch (err) {
    console.log(err);
    const errorState = await handleFail(page);
    return new Response(errorState, response);
  }
};

What could be causing this error? Is there anyway I can debug it? Strange how it only happens rarely.

Hard to even guide you in investigating, without some more details. Could you let us know what site or URL experienced the problem so we can try to correlate that failure with our internal logs and see if there are any clues there?

Can I send you that privately as the site isn’t fully public yet?

:wave: @reasonmark

You can DM me and I will share the site name internally :slight_smile:

Have also seen this message recently

Hi @reasonmark

The second error looks strange and do let me know if that happens again so we can debug it more thoroughly (I’m guessing it does not happen now?)

Regarding the first, it seems that the Edge Function is throwing an uncaught exception as it states. Looking at your code, it might be happening here:

  // Get the page content
  const response = await context.next();
  const page = await response.text();

Notice that particular code snippet is not inside the try-catch logic so if anything were to happen during the request, it wouldn’t be handled.

Can you move that particular snippet of code inside the try-catch so that hopefully we’ll have more insights if this issue happens again?

Yes I thought the same thing so I made that change a few days ago but have still seen both errors since

export default async (request, context) => {
  try {
    // Placeholders in cookies template to replace with dynamic content
    const placeholders = {
      cookieBanner: /<!-- EDGE_FUNCTION_COOKIE_BANNER -->/i,
      analytical: {
        false: /<!-- EDGE_FUNCTION_COOKIE_ANALYTICAL_OFF -->/i,
        true: /<!-- EDGE_FUNCTION_COOKIE_ANALYTICAL_ON -->/i,
      },
      advertisment: {
        false: /<!-- EDGE_FUNCTION_COOKIE_ADVERTISMENT_OFF -->/i,
        true: /<!-- EDGE_FUNCTION_COOKIE_ADVERTISMENT_ON -->/i,
      },
      functional: {
        false: /<!-- EDGE_FUNCTION_COOKIE_FUNCTIONAL_OFF -->/i,
        true: /<!-- EDGE_FUNCTION_COOKIE_FUNCTIONAL_ON -->/i,
      },
    };

    // Don't run this function on asset files
    if (request?.url?.indexOf("/assets/") > -1) return;

    // Get the page content
    const response = await context.next();
    const page = await response.text();

    // Set up new variable to page with replaced content
    let newPage = page;

    // Get users current cookie policy settings
    const policyValue = context.cookies.get("bv-cookie-policy")
      ? JSON.parse(context.cookies.get("bv-cookie-policy"))
      : null;

    // Snippets to replace placeholders with (within function as these are amending depending on the users policy)
    const templates = {
      cookieBanner: `
      <div id="cookie-banner" class="pt-200 pb-200 xl:pt-600 xl:pb-600 bg-supplementary-light">
        <div class="container container--thin">
          <h2 class="heading-3 mb-550">Let us know you agree to cookies</h2>
          <p class="mb-600">We use some essential cookies to make this website work.<br>We'd like to set additional cookies to give you the best online experience.</p>
          <div class="mb-200"><button type="button" data-button="accept-cookies">Accept all cookies</button></div>
          <div><a href="/manage-cookies" class="btn">Manage cookies</a></div>
        </div>
      </div>
      `,
      analytical: {
        false: `<input checked type="radio" name="analytical" id="analytical-false" value="false">`,
        true: `<input type="radio" name="analytical" id="analytical-true" value="true">`,
      },
      advertisment: {
        false: `<input checked type="radio" name="advertisment" id="advertisment-false" value="false">`,
        true: `<input type="radio" name="advertisment" id="advertisment-true" value="true">`,
      },
      functional: {
        false: `<input checked type="radio" name="functional" id="functional-false" value="false">`,
        true: `<input type="radio" name="functional" id="functional-true" value="true">`,
      },
    };

    // Loop through placeholder
    for (const placeholder in placeholders) {
      // Show cookie banner in user has no policy
      if (placeholder === "cookieBanner") {
        const bannerMarkup = !policyValue ? templates[placeholder] : "";
        newPage = newPage.replace(placeholders[placeholder], bannerMarkup);
      }

      // Replace placeholders with radio fields
      if (placeholder !== "cookieBanner") {
        for (const child in placeholders[placeholder]) {
          // If user has a cookie policy, remove all checked attributes then add checked attribute to relevent radio input templates
          if (policyValue) {
            templates[placeholder][child] = templates[placeholder][
              child
            ].replace("<input checked", "<input");

            const selectedPolicyValue = policyValue[placeholder];
            const policyTypeTemplates = templates[placeholder];

            policyTypeTemplates[selectedPolicyValue] = policyTypeTemplates[
              selectedPolicyValue
            ].replace("<input", "<input checked");
          }

          newPage = newPage.replace(
            placeholders[placeholder][child],
            templates[placeholder][child]
          );
        }
      }
    }

    // Return new page
    return new Response(newPage, response);
  } catch (err) {
    return;
  }
};

And did the Edge Function console show you some error messages when you saw the error pages?

No the console is/was completely blank

Hey @reasonmark , we’ve reached out internally to the team who handles Edge Functions so they can take a look and advise further. We’ll let you know what they say, but let us know if you find any new info in the mean time.