I have about 120 devices in LibreNMS and 4 of those devices are NTI environmental sensors that have been attached to LibreNMS for about 3 years. I’ve been able to previously see data from day 1 of adding those devices to Libre but all of a sudden I checked, today, and data for all 4 devices only goes back to May 27, 2026. The Recent events for one of the devices shows 2026-05-27 Sensor Added. I did not delete and re-add all the sensors on the 27th, if I did, I wouldn’t be here. I’m the only admin of this system.
I randomly checked other network devices and I have data that is several years old going back to day 1 of those specific devices. One was a random firewall I clicked on and the other was a random switch that I clicked on.
Whatever happened on 5-27-26 happened to all 4 of my NTI environmental devices.
Can you help me out? I did not change SNMP values or do anything with the devices. It is only these 4 devices (that I can tell, so far) that had their historical data wiped on 5-27. I’m not sure how to find the rrd files, rename/re-associate with the current devices. The handful of random devices that I have checked have all the historical data.
Support was added for additional models and that merge took place on the 26th, but that shouldn’t have caused you to lose data for any existing ones. Were you using Custom OIDs to monitor them, or had you customized your nti.yaml file to add support and it was possibly overwritten?
No customizations were made that I recall, I really don’t modify the system because I’m clueless with coding, files, etc. I would only do something if someone provided cookbook instructions.
Interesting. My devices show up in librenms and I can get to the main device page for each NTI sensor, but when I click on the temp graph it takes me to a page that says “Whoops looks like something went wrong.”
I last checked this page on 7-3-26 and it was working so it looks like some type of update broke the graph page for temp data (at least for temperature, I will check other pages for this device).
I’m fine with the defaults. If I can’t control features/issues then it doesn’t matter if I see the issue daily or the end of the month. Ironically, I was monitoring this specific device over the weekend, but aside from this one off, I don’t monitor the graphs that closely, but I do like the data to be there when I need it. Also, if something breaks on a monthly release I have to wait 1 more month for the fix if a fix is in the works. I prefer how it is today that way the fix arrives w/o having to wait a month. Obviously there are pros/cons to daily vs monthly.
Sure, you just seem sensitive to breakage and most breakage happens in daily and is resolved in a single day or a few. It is very rare a monthly release has a bad issue and we usually release a patch release if it is bad enough.
As for your data. Can you show the full log of both times the sensor was added. We need to know why your sensors changed.
As I mentioned, it is likely your data is in an RRD file on your system and could be recovered.
It seems a contributor did change the indexes in this PR, but it was missed in review.
This PR by the same contributor attempted to change indexes again, but it was caught this time.
So you probably have rrd files named like this in the device rrd folder: sensor-<class>-intSensorEntry.<number>.rrd sensor-<class>-intSensorValue.<number>.rrd
The Entry one holds the old data.
I had an LLM write a merge script for you. No guarantees this won’t delete all your data, etc.
#!/usr/bin/env bash
set -e
CLASSES=("temperature" "humidity")
# Helper function to extract explicit timestamp and data pairs natively
extract_data_via_fetch() {
local file="$1"
# Extract the last 5 years of historical data to capture the entire timeline
rrdtool fetch "$file" AVERAGE --start now-5years --end now | awk '
/^[0-9]+/ {
ts = $1
sub(/:$/, "", ts) # Strip trailing colon from timestamp
v = $2
gsub(/^[ \t]+|[ \t]+$/, "", v)
if (v != "" && tolower(v) != "nan" && v != "-nan") {
print ts "," v
}
}
'
}
for cls in "${CLASSES[@]}"; do
for old in sensor-"$cls"-intSensorEntry.*.rrd; do
[ -e "$old" ] || continue
new="${old/intSensorEntry/intSensorValue}"
bak="${new}.bak"
if [ ! -f "$new" ] && [ ! -f "$bak" ]; then
echo "Skipping ${old}: no matching new file or backup found"
continue
fi
echo "Reconstructing and stitching layout for ${old} -> ${new}"
# 1. Manage the pristine backup file safely
if [ ! -f "$bak" ]; then
cp -p "$new" "$bak"
fi
# 2. Extract and combine datasets into a single sorted sequential stream
combined_data=$(mktemp)
{
extract_data_via_fetch "$old"
extract_data_via_fetch "$bak"
} | sort -nu -t',' -k1,1 > "$combined_data"
if [ ! -s "$combined_data" ]; then
echo " Warning: No data found to merge for ${old}."
rm -f "$combined_data"
continue
fi
# 3. Find the oldest timestamp to use as the database birth date
first_ts=$(head -n 1 "$combined_data" | cut -d',' -f1)
start_origin=$((first_ts - 300))
# 4. Clone exact core parameters from production file layout
step_size=$(rrdtool info "$bak" | awk '/^step =/ {print $3}')
heartbeat=$(rrdtool info "$bak" | awk -F'.' '/^ds\[sensor\].minimal_heartbeat =/ {print $2}' | awk '{print $3}')
# 5. DYNAMICALLY HARVEST ALL 13 RRAs FROM PRODUCTION METADATA
# This loops through all rra definitions and formats them back into RRDtool creation syntax
rra_definitions=$(rrdtool info "$bak" | awk '
/^rra\[[0-9]+\]\.cf =/ { rra=$1; cf=$3; gsub(/"/, "", cf) }
/^rra\[[0-9]+\]\.rows =/ { rows=$3 }
/^rra\[[0-9]+\]\.pdp_per_row =/ { pdp=$3 }
/^rra\[[0-9]+\]\.xff =/ { xff=$3
printf "RRA:%s:%s:%s:%s ", cf, xff, pdp, rows
}
')
# 6. Build a temporary blank clone containing all 13 original archives
tmp_rrd=$(mktemp --suffix=.rrd)
rm -f "$tmp_rrd"
# Dynamically execute the create string with all harvested RRAs
rrdtool create "$tmp_rrd" --start "$start_origin" --step "$step_size" \
DS:sensor:GAUGE:"$heartbeat":U:U \
$rra_definitions
# 7. Stream the chronological data securely straight into the clean structure
# RRDtool will natively handle recalculating the averages, min, max, and last states
while IFS=',' read -r ts val; do
rrdtool update "$tmp_rrd" "${ts}:${val}" 2>/dev/null
done < "$combined_data"
# 8. Replace active file with the complete timeline history
mv "$tmp_rrd" "$new"
chmod 644 "$new"
rm -f "$combined_data"
done
done
echo "Done."
Thanks for doing all that. I used an LLM to assist with the script and the LLM found HTML in the script but once you confirmed the data was likely still there and provided the script, I explained the issue to the LLM and it assisted me with merging the historical data and current data files and now everything is back to showing the historical data.
For anyone else reading, I can’t say that exact script ran and solved the issue, but it did get me in the right direction with the LLM so I’m going to mark that as the answer.