ankane/pghero · error · PgHero::Error

Invalid metric name

Error message

Invalid metric name

What it means

PgHero raises this from the private gcp_stats helper (lib/pghero/methods/system.rb:89) when metric_name fails /\A[a-z\/_]+\z/i. The check exists because metric_name is interpolated directly into the Cloud Monitoring time-series filter string (metric.type = "cloudsql.googleapis.com/database/#{metric_name}"); anything outside letters, underscores, and slashes could break out of the quoted filter. PgHero's own callers only pass fixed names like cpu/utilization, disk/quota, postgresql/num_backends, so this fires when custom code invokes gcp_stats with an arbitrary metric string.

Source

Thrown at lib/pghero/methods/system.rb:89

          data
        else
          raise NotEnabled, "System stats not enabled"
        end
      end

      private

      def gcp_stats(metric_name, duration: nil, period: nil, offset: nil, series: false)
        # TODO DRY with RDS stats
        duration = (duration || 1.hour).to_i
        period = (period || 1.minute).to_i
        offset = (offset || 0).to_i
        end_time = Time.at(((Time.now - offset).to_f / period).ceil * period)
        start_time = end_time - duration

        # validate input since we need to interpolate below
        raise Error, "Invalid metric name" unless /\A[a-z\/_]+\z/i.match?(metric_name)
        raise Error, "Invalid database id" unless /\A[a-z0-9\-:]+\z/i.match?(gcp_database_id)

        # we handle three situations:
        # 1. google-cloud-monitoring-v3
        # 2. google-cloud-monitoring
        # 3. google-apis-monitoring_v3
        begin
          require "google/cloud/monitoring/v3"
        rescue LoadError
          require "google/apis/monitoring_v3"
        end

        # for situations 1 and 2
        # Google::Cloud::Monitoring.metric_service doesn't work for situation 1
        if defined?(Google::Cloud::Monitoring::V3)
          client = Google::Cloud::Monitoring::V3::MetricService::Client.new

          interval = Google::Cloud::Monitoring::V3::TimeInterval.new

View on GitHub (pinned to 7edb57986f)

Solutions

  1. Pass the short slash-form metric name relative to cloudsql.googleapis.com/database/ (e.g. "cpu/utilization", "disk/read_ops_count"), matching the whitelist of letters, underscores, and slashes.
  2. If you must call gcp_stats with a custom metric, normalize the name first: strip the cloudsql.googleapis.com/database/ prefix and replace invalid characters with underscores.
  3. Rescue PgHero::Error around custom metric calls and log the rejected name, since the generic Error class carries no structured details.

Example fix

# before - raises PgHero::Error: Invalid metric name
obj.send(:gcp_stats, "cloudsql.googleapis.com/database/cpu/utilization")

# after - use the short slash-form name (letters, underscore, slash only)
obj.send(:gcp_stats, "cpu/utilization")
Defensive patterns

Strategy: validation

Validate before calling

VALID_GCP_METRIC = /\A[a-z\/_]+\z/i
name = "cloudsql.googleapis.com/database/cpu/utilization".delete_prefix("cloudsql.googleapis.com/database/")
PgHero.send(:gcp_stats, name) if VALID_GCP_METRIC.match?(name)

Type guard

def valid_gcp_metric_name?(name)
  name.is_a?(String) && name.match?(/\A[a-z\/_]+\z/i)
end

Try / catch

begin
  PgHero.send(:gcp_stats, metric_name)
rescue PgHero::Error => e
  raise unless e.message == "Invalid metric name"
  Rails.logger.error("Rejected GCP metric name: #{metric_name.inspect}")
  {}
end

Prevention

When it happens

Trigger: Calling the private helper directly (obj.send(:gcp_stats, "disk/bytes_used")) or extending PgHero with new metrics whose names contain dots, digits, spaces, or quotes (e.g. "v2/cpu", "disk.io", "cpu utilization") — all fail the letters/underscore/slash-only whitelist.

Common situations: Plugins or monkey-patches adding custom Cloud SQL metrics to PgHero's system_stats metrics map; copy-pasted metric names from the GCP console (console paths use dots, e.g. cloudsql.googleapis.com/database/cpu/utilization) passed as the full dotted path instead of the trailing slash form.

Related errors


AI-assisted analysis of ankane/pghero@7edb57986f (2026-08-21). Data as JSON: /api/errors/1fb6dc3f10a5e4cd. Report an issue: GitHub.