diff --git a/lib/pengine/pe_health.c b/lib/pengine/pe_health.c
index a6e2bdcd4c..6848a21b4f 100644
--- a/lib/pengine/pe_health.c
+++ b/lib/pengine/pe_health.c
@@ -1,158 +1,182 @@
 /*
  * Copyright 2004-2024 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 
 #include <crm/pengine/status.h>
 #include <crm/pengine/internal.h>
 #include "pe_status_private.h"
 
 /*!
  * \internal
  * \brief Set the node health values to use for \c PCMK_VALUE_RED,
  *        \c PCMK_VALUE_YELLOW, and \c PCMK_VALUE_GREEN
  *
  * \param[in,out] scheduler  Scheduler data
  */
 void
 pe__unpack_node_health_scores(pcmk_scheduler_t *scheduler)
 {
     switch (pe__health_strategy(scheduler)) {
         case pcmk__health_strategy_none:
             pcmk__score_red = 0;
             pcmk__score_yellow = 0;
             pcmk__score_green = 0;
             break;
 
         case pcmk__health_strategy_no_red:
             pcmk__score_red = -PCMK_SCORE_INFINITY;
             pcmk__score_yellow = 0;
             pcmk__score_green = 0;
             break;
 
         case pcmk__health_strategy_only_green:
             pcmk__score_red = -PCMK_SCORE_INFINITY;
             pcmk__score_yellow = -PCMK_SCORE_INFINITY;
             pcmk__score_green = 0;
             break;
 
         default: // progressive or custom
             pcmk__score_red = pcmk__health_score(PCMK_OPT_NODE_HEALTH_RED,
                                                  scheduler);
             pcmk__score_green = pcmk__health_score(PCMK_OPT_NODE_HEALTH_GREEN,
                                                    scheduler);
             pcmk__score_yellow = pcmk__health_score(PCMK_OPT_NODE_HEALTH_YELLOW,
                                                     scheduler);
             break;
     }
 
     if ((pcmk__score_red != 0) || (pcmk__score_yellow != 0)
         || (pcmk__score_green != 0)) {
         crm_debug("Values of node health scores: "
                   PCMK_VALUE_RED "=%d "
                   PCMK_VALUE_YELLOW "=%d "
                   PCMK_VALUE_GREEN "=%d",
                   pcmk__score_red, pcmk__score_yellow, pcmk__score_green);
     }
 }
 
+struct health_sum {
+    const pcmk_node_t *node; // Node that health score is being summed for
+    int sum;                 // Sum of health scores checked so far
+};
+
 /*!
  * \internal
  * \brief Add node attribute value to an integer, if it is a health attribute
  *
  * \param[in]     key        Name of node attribute
  * \param[in]     value      String value of node attribute
  * \param[in,out] user_data  Address of integer to which \p value should be
  *                           added if \p key is a node health attribute
  */
 static void
 add_node_health_value(gpointer key, gpointer value, gpointer user_data)
 {
     if (pcmk__starts_with((const char *) key, "#health")) {
-        int score = char2score((const char *) value);
-        int *health = (int *) user_data;
+        struct health_sum *health_sum = user_data;
+        int score = 0;
+        int rc = pcmk_parse_score((const char *) value, &score, 0);
+
+        if (rc != pcmk_rc_ok) {
+            crm_warn("Ignoring %s for %s because '%s' is not a valid value: %s",
+                     (const char *) key, pcmk__node_name(health_sum->node),
+                     (const char *) value, pcmk_rc_str(rc));
+            return;
+        }
 
-        *health = pcmk__add_scores(score, *health);
+        health_sum->sum = pcmk__add_scores(score, health_sum->sum);
         crm_trace("Combined '%s' into node health score (now %s)",
-                  (const char *) value, pcmk_readable_score(*health));
+                  (const char *) value, pcmk_readable_score(health_sum->sum));
     }
 }
 
 /*!
  * \internal
  * \brief Sum a node's health attribute scores
  *
  * \param[in] node         Node whose health attributes should be added
  * \param[in] base_health  Add this number to the total
  *
  * \return Sum of all health attribute scores of \p node plus \p base_health
  */
 int
 pe__sum_node_health_scores(const pcmk_node_t *node, int base_health)
 {
+    struct health_sum health_sum = { node, base_health, };
+
     CRM_ASSERT(node != NULL);
     g_hash_table_foreach(node->details->attrs, add_node_health_value,
-                         &base_health);
-    return base_health;
+                         &health_sum);
+    return health_sum.sum;
 }
 
 /*!
  * \internal
  * \brief Check the general health status for a node
  *
  * \param[in,out] node  Node to check
  *
  * \return  A negative value if any health attribute for \p node is red,
  *          otherwise 0 if any attribute is yellow, otherwise a positive value.
  */
 int
 pe__node_health(pcmk_node_t *node)
 {
     GHashTableIter iter;
     const char *name = NULL;
     const char *value = NULL;
     enum pcmk__health_strategy strategy;
     int score = 0;
     int rc = 1;
 
     CRM_ASSERT(node != NULL);
 
     strategy = pe__health_strategy(node->details->data_set);
     if (strategy == pcmk__health_strategy_none) {
         return rc;
     }
 
     g_hash_table_iter_init(&iter, node->details->attrs);
     while (g_hash_table_iter_next(&iter, (gpointer *) &name,
                                   (gpointer *) &value)) {
         if (pcmk__starts_with(name, "#health")) {
+            int parse_rc = pcmk_rc_ok;
+
             /* It's possible that pcmk__score_red equals pcmk__score_yellow,
              * or pcmk__score_yellow equals pcmk__score_green, so check the
              * textual value first to be able to distinguish those.
              */
             if (pcmk__str_eq(value, PCMK_VALUE_RED, pcmk__str_casei)) {
                 return -1;
             } else if (pcmk__str_eq(value, PCMK_VALUE_YELLOW,
                                     pcmk__str_casei)) {
                 rc = 0;
                 continue;
             }
 
+            parse_rc = pcmk_parse_score(value, &score, 0);
+            if (parse_rc != pcmk_rc_ok) {
+                crm_warn("Treating %s for %s as 0 "
+                         "because '%s' is not a valid value: %s",
+                         name, pcmk__node_name(node), value,
+                         pcmk_rc_str(parse_rc));
+            }
+
             // The value is an integer, so compare numerically
-            score = char2score(value);
             if (score <= pcmk__score_red) {
                 return -1;
             } else if ((score <= pcmk__score_yellow)
                        && (pcmk__score_yellow != pcmk__score_green)) {
                 rc = 0;
             }
         }
     }
     return rc;
 }