diff --git a/public/js/appChart.js b/public/js/appChart.js
index d7acae3a18fd261662c3cce4bf6c46f9c1dca31f..7ff9b4a6e0685f76adc76a62f8d9dc7d129680f8 100644
--- a/public/js/appChart.js
+++ b/public/js/appChart.js
@@ -228,18 +228,35 @@ const drawChartUsingSelectedOptions = async function () {
             )
           );
 
+    // If there is an error in fetching metadata + observations (Case 1: raw observations)
+    // the returned array will have this structure: [[undefined, undefined], undefined]
+    // Note that the second element is not an array as we would expect but is a
+    // a single `undefined` value
+    if (typeof observationsRawPlusMetadataArr[0][0] === "undefined") {
+      throw new Error(
+        `There was a problem in fetching metadata and observations`
+      );
+    }
+
+    // If there is an error in fetching metadata + observations (Case 2: temperature difference, dT)
+    // a single `undefined` value instead of an array (as we would expect) will be returned
+    if (typeof observationsTempDiffPlusMetadataArr === "undefined")
+      throw new Error(
+        `There was a problem in calculating the temperature difference (dT).\nThis is most likely due to a problem in fetching metadata and observations`
+      );
+
     // Extract the combined arrays for observations and metadata / raw observations
-    const [observationsRawNestedArr, metadataTempDiffNestedArr] =
+    const [observationsRawNestedArr, metadataRawNestedArr] =
       observationsRawPlusMetadataArr;
 
     // Extract the combined arrays for observations and metadata / temperature difference (dT)
-    const [observationsNestedComputedArr, metadataNestedComputedArr] =
+    const [observationsTempDiffNestedArr, metadataTempDiffNestedArr] =
       observationsTempDiffPlusMetadataArr;
 
     // Create a combined array of observations and metadata
     const observationsPlusMetadataCombined = [
-      [...observationsRawNestedArr, ...observationsNestedComputedArr],
-      [...metadataTempDiffNestedArr, ...metadataNestedComputedArr],
+      [...observationsRawNestedArr, ...observationsTempDiffNestedArr],
+      [...metadataRawNestedArr, ...metadataTempDiffNestedArr],
     ];
 
     const [observationsComboNestedArr, metadataComboNestedArr] =
@@ -384,6 +401,9 @@ const drawChartUsingSelectedOptions = async function () {
     }
   } catch (err) {
     console.error(err);
+
+    // Display a dialog window with the error message
+    alert(err);
   } finally {
     // Enable the 'draw chart' button
     enableDrawChartButton();
diff --git a/public/js/src_modules/aggregateHelpers.mjs b/public/js/src_modules/aggregateHelpers.mjs
index 49f2ac91742f8942c33e1dcfaf19a0aff322ffb0..e81f0cbd5d1c7011e782f4a999fb734371a2444f 100644
--- a/public/js/src_modules/aggregateHelpers.mjs
+++ b/public/js/src_modules/aggregateHelpers.mjs
@@ -252,58 +252,67 @@ const extractObservationValuesWithinMonthInterval = function (
   samplingRate,
   calendarMonthStr
 ) {
-  // Extract the year and month digits from the calendar month string
-  const [yearNum, monthNum] =
-    extractMonthYearDigitsFromCalendarMonthString(calendarMonthStr);
+  try {
+    // Extract the year and month digits from the calendar month string
+    const [yearNum, monthNum] =
+      extractMonthYearDigitsFromCalendarMonthString(calendarMonthStr);
 
-  // All the months start on the first
-  const startDateStr = `${calendarMonthStr}-01`;
+    // All the months start on the first
+    const startDateStr = `${calendarMonthStr}-01`;
 
-  if (monthNum < 1 || monthNum > 12) {
-    throw new Error("The specified digit for the month of the year is invalid");
-  }
-  // February
-  else if (monthNum === 2) {
-    // Leap year
-    if (checkIfLeapYear(yearNum)) {
+    if (monthNum < 1 || monthNum > 12) {
+      throw new Error(
+        "The specified digit for the month of the year is invalid"
+      );
+    }
+    // February
+    else if (monthNum === 2) {
+      // Leap year
+      if (checkIfLeapYear(yearNum)) {
+        return extractObservationValuesWithinDatesInterval(
+          obsArray,
+          samplingRate,
+          startDateStr,
+          `${calendarMonthStr}-29`
+        );
+      }
+      // Non-leap year
+      else {
+        return extractObservationValuesWithinDatesInterval(
+          obsArray,
+          samplingRate,
+          startDateStr,
+          `${calendarMonthStr}-28`
+        );
+      }
+    }
+    // Months with 30 days
+    else if (
+      monthNum === 4 ||
+      monthNum === 6 ||
+      monthNum === 9 ||
+      monthNum === 11
+    ) {
       return extractObservationValuesWithinDatesInterval(
         obsArray,
         samplingRate,
         startDateStr,
-        `${calendarMonthStr}-29`
+        `${calendarMonthStr}-30`
       );
     }
-    // Non-leap year
+    // Months with 31 days
     else {
       return extractObservationValuesWithinDatesInterval(
         obsArray,
         samplingRate,
         startDateStr,
-        `${calendarMonthStr}-28`
+        `${calendarMonthStr}-31`
       );
     }
-  }
-  // Months with 30 days
-  else if (
-    monthNum === 4 ||
-    monthNum === 6 ||
-    monthNum === 9 ||
-    monthNum === 11
-  ) {
-    return extractObservationValuesWithinDatesInterval(
-      obsArray,
-      samplingRate,
-      startDateStr,
-      `${calendarMonthStr}-30`
-    );
-  }
-  // Months with 31 days
-  else {
-    return extractObservationValuesWithinDatesInterval(
-      obsArray,
-      samplingRate,
-      startDateStr,
-      `${calendarMonthStr}-31`
+  } catch (err) {
+    // Rethrow errors from `getIndexOfStartTimestamp` or `getIndexOfEndTimestamp` functions
+    throw new Error(
+      `${err.message} \nCurrently, the monthly aggregation does not support partial calendar months`
     );
   }
 };
diff --git a/public/js/src_modules/calculateTemperatureDiff.mjs b/public/js/src_modules/calculateTemperatureDiff.mjs
index 615c0238a117fca5463000d8cf3dfddacfa81cd4..b63dc94260e557d4e45d93f5cb519127ef3f2a55 100644
--- a/public/js/src_modules/calculateTemperatureDiff.mjs
+++ b/public/js/src_modules/calculateTemperatureDiff.mjs
@@ -175,6 +175,16 @@ export const calculateVorlaufMinusRuecklaufTemperature = async function (
           bldgDataPtSamplingRateNestedArr
         );
 
+      // If there is an error in fetching metadata + observations,
+      // the returned array will have this structure: [[undefined, undefined], undefined]
+      // Note that the second element is not an array as we would expect but is a
+      // a single `undefined` value
+      if (typeof observationsPlusMetadata[0][0] === "undefined") {
+        throw new Error(
+          `There was a problem in calculating the temperature difference (dT).\nThis is most likely due to a problem in fetching metadata and observations`
+        );
+      }
+
       // dT observations (timestamp + value)
       const vorlaufMinusRuecklaufTemperatureObs =
         calculateVorlaufMinusRuecklaufTemperatureObservations(
diff --git a/public/js/src_modules/fetchData.mjs b/public/js/src_modules/fetchData.mjs
index 9064ed667435dffc55dcc2d7faa5bf408a385f63..2d6cbabab03a296b0d34fb96846f341511cd535f 100644
--- a/public/js/src_modules/fetchData.mjs
+++ b/public/js/src_modules/fetchData.mjs
@@ -24,44 +24,73 @@ const createObservationsUrl = function (baseUrl, datastreamID) {
   return `${baseUrl}/Datastreams(${datastreamID})/Observations`;
 };
 
+/**
+ * Perform a GET request to fetch a single Datastream using the Axios library
+ *
+ * @param {String} urlDatastream A URL that fetches a single Datastream from an STA instance
+ * @returns {Promise} A promise that contains the Datastream metadata when fulfilled
+ */
+const getDatastream = function (urlDatastream) {
+  return axios.get(urlDatastream);
+};
+
 /**
  * Perform a GET request using the Axios library
  * @param {String} urlObservations A URL that fetches Observations from an STA instance
  * @param {Object} urlParamObj The URL parameters to be sent together with the GET request
  * @returns {Promise} A promise that contains the first page of results when fulfilled
  */
-const performGetRequestUsingAxios = function (urlObservations, urlParamObj) {
+const getObservations = function (urlObservations, urlParamObj) {
   return axios.get(urlObservations, {
     params: urlParamObj,
   });
 };
 
 /**
- * Retrieve the metadata for a single datastream
+ * Extract the metadata from a single datastream
  * @async
  * @param {String} urlDatastream A URL that fetches a Datastream from an STA instance
  * @returns {Promise} A promise that contains a metadata object for a Datastream when fulfilled
  */
-const getMetadataFromSingleDatastream = async function (urlDatastream) {
+const extractMetadataFromSingleDatastream = async function (urlDatastream) {
   try {
     // Extract properties of interest
     const {
       data: { description, name, unitOfMeasurement },
-    } = await performGetRequestUsingAxios(urlDatastream);
+    } = await getDatastream(urlDatastream);
 
     return { description, name, unitOfMeasurement };
   } catch (err) {
-    console.error(err);
+    // Server responded with status code outside of 2xx range
+    if (err.response) {
+      throw new Error(
+        `The request to fetch Datastream metadata was made but the server responded with: \n${err.message}`
+      );
+    }
+    // Request was made but no response was received
+    else if (err.request) {
+      throw new Error(
+        `The request to fetch Datastream metadata was made but no response was received: \n${err.message}`
+      );
+    }
+    // Problem with setting up the request
+    else {
+      throw new Error(
+        `There was a problem setting up the request to fetch Datastream metadata: \n${err.message}`
+      );
+    }
   }
 };
 
 /**
- * Retrieve metadata from multiple datastreams
+ * Extract the metadata from multiple datastreams
  * @async
  * @param {Array} datastreamsUrlArr An array that contains N Datastream URL strings
  * @returns {Promise} A promise that contains an array of N Datastream metadata objects when fulfilled
  */
-const getMetadataFromMultipleDatastreams = async function (datastreamsUrlArr) {
+const extractMetadataFromMultipleDatastreams = async function (
+  datastreamsUrlArr
+) {
   try {
     // Array to store our final result
     const datastreamMetadataArr = [];
@@ -69,7 +98,7 @@ const getMetadataFromMultipleDatastreams = async function (datastreamsUrlArr) {
     // Use for/of loop - we need to maintain the order of execution of the async operations
     for (const datastreamUrl of datastreamsUrlArr) {
       // Metadata from a single Datastream
-      const datastreamMetadata = await getMetadataFromSingleDatastream(
+      const datastreamMetadata = await extractMetadataFromSingleDatastream(
         datastreamUrl
       );
       datastreamMetadataArr.push(datastreamMetadata);
@@ -109,7 +138,24 @@ const combineResultsFromAllPages = async function (httpGetRequestPromise) {
       return lastSuccess;
     }
   } catch (err) {
-    console.error(err);
+    // Server responded with status code outside of 2xx range
+    if (err.response) {
+      throw new Error(
+        `The request to fetch Observations was made but the server responded with: \n${err.message}`
+      );
+    }
+    // Request was made but no response was received
+    else if (err.request) {
+      throw new Error(
+        `The request to fetch Observations was made but no response was received: \n${err.message}`
+      );
+    }
+    // Problem with setting up the request
+    else {
+      throw new Error(
+        `There was a problem setting up the request to fetch Observations: \n${err.message}`
+      );
+    }
   }
 };
 
@@ -208,7 +254,7 @@ const getMetadataPlusObservationsFromSingleOrMultipleDatastreams =
       // Promise objects - Observations
       const observationsPromisesArr = observationsUrlArr.map((obsUrl) =>
         extractCombinedObservationsFromAllPages(
-          performGetRequestUsingAxios(obsUrl, urlParamObj)
+          getObservations(obsUrl, urlParamObj)
         )
       );
 
@@ -218,7 +264,7 @@ const getMetadataPlusObservationsFromSingleOrMultipleDatastreams =
       );
 
       // Metadata array
-      const metadataArr = await getMetadataFromMultipleDatastreams(
+      const metadataArr = await extractMetadataFromMultipleDatastreams(
         datastreamsUrlArr
       );