Skip to content

property_tree raises SystemStackError for ontologies with a self-referential property parent #56

Description

@alexskr

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:

  • NIFSTDhttp://www.w3.org/2004/02/skos/core#definition (parents: ["skos:note", "skos:definition"])
  • IOBChttp://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:

  1. 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
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions