Bug: bill graphs API endpoint 500s on 26.5.0 - Device::displayName() returns null

Summary

Every call to GET /api/v0/bills/{id}/graphs/bits returns HTTP 500 since the upgrade to 26.5.0. The 26.4.x line worked. The same endpoint with the same parameters and same API token was returning SVG/PNG correctly until the auto-update.

Reproduce

  1. Generate an API token with read access.
  2. Call any bill’s graph endpoint:
curl -s -H "X-Auth-Token: $TOKEN" -o /tmp/g -w "HTTP %{http_code}\n" 
"https://librenms.example.com/api/v0/bills/77/graphs/bits?from=((((
(((date +%s) - 604800))&to=$(date +%s)&width=900&height=260"
cat /tmp/grs/

Returns:

HTTP 500
{"message":"Server Error"}

Confirmed reproducible with multiple distinct bills (cdr and quota types), on different ports and devices. The /api/v0/bills/{id} (stats only) endpoint still works — only the graph subroute crashes.

Stack trace from /opt/librenms/logs/librenms.log

The return type was tightened to non-nullable string in 26.5.0. But GraphParameters::defaultTitle() for bill graphs passes a new Device() (empty model — bills are aggregates of ports, not tied to a single device), so both $this->display and $this->hostname are null. The function returns null and the type check throws.

Suggested fix

Either:

a) Make displayName() tolerate null fields:

public function displayName(): string
{
    return (string) ($this->display ?: $this->hostname ?: $this->ip ?: '');
}

b) Or, in GraphParameters::defaultTitle(), skip the device-title path when no device is associated.

(a) is the smaller patch and is what I’m running locally as a stop-gap.

Impact

The bill graph API endpoint is the documented way to embed bandwidth graphs in external tools (WHMCS modules, NOC dashboards, customer portals). 26.5.0 broke all of them at once.

Better to just fix the graph.

Did you perhaps delete a device and not the bills associated with it?

(Edited to be less harsh.)

Thanks for your feedback. Nothing was deleted. Nothing has changed. The only change is a LibreNMS update last night. Happy share any needed details to debug this.

$title = DeviceCache::getPrimary()->display ?: ucfirst($this->type);

How about that?

Also the bill works when viewed directly via libre url. It’s only api that stopped working.

@Kez Try my fix above.

diff --git a/LibreNMS/Data/Graphing/GraphParameters.php b/LibreNMS/Data/Graphing/GraphParameters.php
index b6f84c1dd4..c662b0bd58 100644
--- a/LibreNMS/Data/Graphing/GraphParameters.php
+++ b/LibreNMS/Data/Graphing/GraphParameters.php
@@ -292,7 +292,7 @@ class GraphParameters implements \Stringable
 
     private function defaultTitle(): string
     {
-        $title = DeviceCache::getPrimary()->displayName() ?: ucfirst($this->type);
+        $title = DeviceCache::getPrimary()->display ?: ucfirst($this->type);
         $title .= '::';
         $title .= Str::title(str_replace('_', ' ', $this->subtype));
 

1 Like

Thanks, will try :slight_smile:

It’s already fixed in master and 26.5.1 is out so just update

1 Like

Thanks for fixing so quickly

Confirmed fixed, thanks again!