property_tree raises SystemStackError for ontologies with a self-referential property parent
Summary
Rendering the property tree for certain ontologies crashes with SystemStackError (stack level too deep). The trigger is a property whose parents list contains its own IRI (a subPropertyOf pointing at itself). Ontology#property_tree builds the tree client-side and appends each property under every parent, so a self-referential parent produces a self-child edge. Property#to_hash then recurses on itself indefinitely until the stack is exhausted.
Confirmed in production (data.bioontology.org) for:
- NIFSTD —
http://www.w3.org/2004/02/skos/core#definition (parents: ["skos:note", "skos:definition"])
- IOBC —
http://www.w3.org/2004/02/skos/core#altLabel, http://purl.org/dc/elements/1.1/creator, http://www.w3.org/2004/02/skos/core#definition
Both ontologies contain only self-loops — no multi-node cycles.
Impact
Any UI request that renders the property tree for an affected ontology returns 500 Internal Server Error. Observed via ConceptsController#property_tree in bioportal_web_ui:
Processing by ConceptsController#property_tree as */*
Parameters: {"apikey"=>"", "ontology"=>"NIFSTD", "no_context"=>"true", "concept"=>{}}
Completed 500 Internal Server Error in 6048ms
SystemStackError (stack level too deep):
app/controllers/concepts_controller.rb:135:in `property_tree'
Client stack trace (truncated, ~3000 frames):
lib/ontologies_api_client/models/property.rb:47:in `to_hash'
lib/ontologies_api_client/models/property.rb:47:in `block in to_hash'
lib/ontologies_api_client/models/property.rb:47:in `map'
lib/ontologies_api_client/models/property.rb:47:in `to_hash'
... (repeats until stack exhausted)
lib/ontologies_api_client/base.rb:100:in `map'
lib/ontologies_api_client/base.rb:100:in `marshal_dump'
lib/ontologies_api_client/base.rb:85:in `to_hash'
Root cause
lib/ontologies_api_client/models/ontology.rb, property_tree:
prop.parents.each { |par| properties[par].children << prop if properties[par] }
When par == prop.id this appends the property to its own children. Property#to_hash (lib/ontologies_api_client/models/property.rb) then maps over children recursively with no cycle guard:
hash[:children] = self.children.map { |p| p.to_hash }
Steps to reproduce
LinkedData::Client.config { |c| c.rest_url = "https://data.bioontology.org"; c.apikey = ENV["API_KEY"] }
ont = LinkedData::Client::Models::Ontology.find_by_acronym("NIFSTD").first
ont.property_tree.to_hash # => SystemStackError
Confirm the offending data directly:
p = ont.explore.properties.find { |x| x.id == "http://www.w3.org/2004/02/skos/core#definition" }
p.parents.include?(p.id) # => true
Proposed fix
Two layers:
-
property_tree — skip self-referential parents so the constructed tree is acyclic for all consumers (jstree rendering, to_hash, …):
prop.parents.each do |par|
next if par == prop.id
properties[par].children << prop if properties[par]
end
-
Property#to_hash — carry a visited set as defense-in-depth, which also covers hypothetical multi-node cycles (mutual subPropertyOf, A→B→A) that the self-edge check alone would not catch.
Verified end-to-end against production after the fix: NIFSTD serializes 1950 tree nodes and IOBC 86, with no SystemStackError.
Notes
The self-referential parent is a data artifact in the affected ontologies, but the client should not crash on it. A regression test that builds a Property with a self-referential child and asserts to_hash terminates would guard against future regressions without requiring a live API call.
Affected version: 2.9.0.
property_treeraisesSystemStackErrorfor ontologies with a self-referential property parentSummary
Rendering the property tree for certain ontologies crashes with
SystemStackError (stack level too deep). The trigger is a property whoseparentslist contains its own IRI (asubPropertyOfpointing at itself).Ontology#property_treebuilds the tree client-side and appends each property under every parent, so a self-referential parent produces a self-child edge.Property#to_hashthen recurses on itself indefinitely until the stack is exhausted.Confirmed in production (
data.bioontology.org) for:http://www.w3.org/2004/02/skos/core#definition(parents: ["skos:note", "skos:definition"])http://www.w3.org/2004/02/skos/core#altLabel,http://purl.org/dc/elements/1.1/creator,http://www.w3.org/2004/02/skos/core#definitionBoth ontologies contain only self-loops — no multi-node cycles.
Impact
Any UI request that renders the property tree for an affected ontology returns
500 Internal Server Error. Observed viaConceptsController#property_treeinbioportal_web_ui:Client stack trace (truncated, ~3000 frames):
Root cause
lib/ontologies_api_client/models/ontology.rb,property_tree:When
par == prop.idthis appends the property to its ownchildren.Property#to_hash(lib/ontologies_api_client/models/property.rb) then maps overchildrenrecursively with no cycle guard:Steps to reproduce
Confirm the offending data directly:
Proposed fix
Two layers:
property_tree— skip self-referential parents so the constructed tree is acyclic for all consumers (jstree rendering,to_hash, …):Property#to_hash— carry avisitedset as defense-in-depth, which also covers hypothetical multi-node cycles (mutualsubPropertyOf, A→B→A) that the self-edge check alone would not catch.Verified end-to-end against production after the fix: NIFSTD serializes 1950 tree nodes and IOBC 86, with no
SystemStackError.Notes
The self-referential parent is a data artifact in the affected ontologies, but the client should not crash on it. A regression test that builds a
Propertywith a self-referential child and assertsto_hashterminates would guard against future regressions without requiring a live API call.Affected version: 2.9.0.