thingsboard/thingsboard · error · IllegalArgumentException
Perimeter attribute '{}' not found for Zone with id: {}
Error message
Perimeter attribute '{}' not found for Zone with id: {} What it means
The zone's perimeter attribute was found as an AttributeKvEntry, but getValueAsString() returned null, meaning the attribute row exists with a null value (or a lookup returned an entry without a value). GeofencingZoneState rejects this because a null perimeter cannot be parsed into PerimeterDefinition. It indicates an empty/corrupt attribute rather than a missing key (a missing key normally yields an empty Optional earlier in the pipeline).
Source
Thrown at application/src/main/java/org/thingsboard/server/service/cf/ctx/state/geofencing/GeofencingZoneState.java:54
private final EntityId zoneId;
private long ts;
private Long version;
private PerimeterDefinition perimeterDefinition;
@EqualsAndHashCode.Exclude
private GeofencingPresenceStatus lastPresence;
public GeofencingZoneState(EntityId zoneId, KvEntry entry) {
this.zoneId = zoneId;
if (!(entry instanceof AttributeKvEntry attributeKvEntry)) {
throw new IllegalArgumentException("Invalid perimeter data source for zone with id: " + zoneId + ". Perimeter definition must be stored as attribute!");
}
this.ts = attributeKvEntry.getLastUpdateTs();
this.version = attributeKvEntry.getVersion();
if (entry.getValueAsString() == null) {
throw new IllegalArgumentException("Perimeter attribute '" + entry.getKey() + "' not found for Zone with id: " + zoneId);
}
this.perimeterDefinition = JacksonUtil.fromString(entry.getValueAsString(), PerimeterDefinition.class,
"Invalid perimeter definition format for Zone with id: " + zoneId + ". Failed to parse attribute '" + entry.getKey() + "'");
}
public GeofencingZoneState(GeofencingZoneProto proto) {
this.zoneId = ProtoUtils.fromProto(proto.getZoneId());
this.ts = proto.getTs();
this.version = proto.getVersion();
this.perimeterDefinition = JacksonUtil.fromString(proto.getPerimeterDefinition(), PerimeterDefinition.class);
if (proto.hasInside()) {
this.lastPresence = proto.getInside() ? INSIDE : OUTSIDE;
}
}
public boolean update(GeofencingZoneState newZoneState) {
if (newZoneState.getTs() <= this.ts) {
return false;View on GitHub (pinned to 45c30e83fa)
Solutions
- Re-save the perimeter attribute with a valid PerimeterDefinition JSON (circle or polygon with coordinates) for the zone entity
- Audit rule-engine actions or integrations that write to the perimeter attribute key and stop them from writing null/empty values
- If the DB row is corrupt, fix it in the DB (set a valid JSON string) or delete the row so the zone is treated as unconfigured rather than broken
- Add a guard in the calling code: skip evaluation of zones whose perimeter attribute is absent or null, and log the zone id
Example fix
// before
new GeofencingZoneState(zoneId, entry); // entry.getValueAsString() == null -> throws
// after
if (entry.getValueAsString() == null) {
log.warn("[{}] Perimeter attribute '{}' is empty, skipping zone", zoneId, entry.getKey());
return;
}
new GeofencingZoneState(zoneId, entry); Defensive patterns
Strategy: validation
Validate before calling
String v = entry.getValueAsString();
if (v == null || v.isBlank()) {
log.warn("[{}] perimeter attribute '{}' empty", zoneId, entry.getKey());
continue; // skip zone
} Type guard
boolean hasPerimeterValue(KvEntry e) { return e.getValueAsString() != null && !e.getValueAsString().isBlank(); } Try / catch
catch (IllegalArgumentException e) if message contains "not found for Zone": treat zone as unconfigured, skip and log; alert on volume of skips.
Prevention
- Guard rule chains that delete/null perimeter attributes
- Validate PerimeterDefinition JSON with a schema check before saving the attribute
- Monitor for null-valued attributes on zone entities
When it happens
Trigger: The perimeter server attribute was written with an explicit null value (e.g. via saveAttributes with a Json null, or a rule node that wrote an empty string cleared to null); a DB migration or manual DB edit left the attribute_kvs row with NULL str_value for the perimeter key; deletion races where the entry is fetched while being invalidated.
Common situations: Rule-engine chain that deletes or nulls the perimeter attribute when a device leaves a zone; direct SQL updates to attribute_kvs; partial attribute writes interrupted mid-transaction; test setups that create the attribute key without a value.
Related errors
- Invalid perimeter data source for zone with id: {}. Perimete
- Unsupported argument entry type for {} argument: {}. Only SI
- Unsupported argument entry type for {} argument: {}. Only GE
- Zone group configuration is missing for the: {}
- Failed to find claimingAllowed attribute for device [{}] or
AI-assisted analysis of thingsboard/thingsboard@45c30e83fa (2026-08-14).
Data as JSON: /api/errors/f26b9541f872679f.
Report an issue: GitHub.