Skip to content

Fix NPE when saving a project with an empty text-equals filter value - #3236

Merged
mbastian merged 1 commit into
masterfrom
fix/filter-save-null-pattern-npe
Aug 23, 2026
Merged

mbastian merged 1 commit into
masterfrom
fix/filter-save-null-pattern-npe

Conversation

@mbastian

Copy link
Copy Markdown
Member

Description

When a user adds an "Attributes > Equal" filter for a text column via the Filters panel but never types a value into it before saving the project, the save silently produces broken output today — and on some XML stream implementations, throws an exception in the process.

The root cause is in EqualStringFilter (modules/FiltersPlugin/src/main/java/org/gephi/filters/plugin/attribute/AttributeEqualBuilder.java): its pattern field has no initializer and is never set unless the user calls setPattern(...). Sibling filters like EgoBuilder.EgoFilter default their equivalent field to "", but this one doesn't, so pattern stays null for a freshly added, untouched filter.

When the project is saved, FilterModelPersistenceProvider.writeParameter (modules/FiltersImpl/src/main/java/org/gephi/filters/FilterModelPersistenceProvider.java:168) serializes every filter property by calling Serialization.toText(value, type) and passing the result straight to writer.writeCharacters(...). Serialization.toText returns null for a null input value by design, so writeCharacters(null) gets called. Depending on the XMLStreamWriter implementation in use, that throws an NPE — this is what shows up in Sentry as GEPHI-5SY.

The fix is two-fold and minimal:

  • writeParameter now guards the serialized text and writes "" instead of null, so no filter property (not just this one) can ever push a null into the XML writer.
  • EqualStringFilter.pattern now defaults to "", matching EgoFilter, so the null case doesn't occur in the first place for this filter.

https://gephi.sentry.io/issues/GEPHI-5SY

Checklist

  • Merged with master beforehand

Added tests?

  • 👍 yes

Added AttributeEqualBuilderTest.testEqualStringFilterDefaultPatternIsNotNull (FiltersPlugin), which fails before the fix (asserts the filter built via AttributeEqualBuilder never has a null pattern), and a persistence round-trip test in PersistenceProviderTest covering an EqualStringFilter left without a pattern.

Added to documentation?

  • 🙅 no, because they aren't needed

EqualStringFilter's pattern field had no default, so a filter added to
the panel but left empty stayed null until the user typed something.
Saving the project then serialized that null straight into the XML
writer's writeCharacters call, which throws on some StAX
implementations. Default pattern to "" like the sibling EgoFilter does,
and guard writeParameter so a null serialized value never reaches the
writer regardless of which filter produced it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@mbastian

Copy link
Copy Markdown
Member Author

The fix looks correct and minimal for the reported crash (EqualStringFilter.pattern defaulting to "" plus the null-guard in FilterModelPersistenceProvider.writeParameter), and the round-trip test correctly exercises the previously-broken path.

However, there's an identical unguarded call site that this PR doesn't touch: LayoutModelImpl.writeXML (modules/LayoutAPI/src/main/java/org/gephi/layout/LayoutModelImpl.java:311):

for (Entry<LayoutPropertyKey, Object> entry : savedProperties.entrySet()) {
    if (entry.getValue() != null) {
        ...
        writer.writeCharacters(Serialization.getValueAsText(entry.getValue()));
        ...

The entry.getValue() != null check only guards against a null value, but Serialization.toText(...) (which getValueAsText delegates to) can still return null for a non-null value whenever PropertyEditorManager.findEditor(valueClass) finds no registered editor for that property's type (see the final else { return null; } branch in Serialization.toText). That happens for any layout property whose type isn't a primitive/wrapper/Number/array/Color/Font/String and has no PropertyEditor registered — e.g. a custom or enum-typed property from a third-party layout plugin — which would hit writeCharacters(null) and NPE exactly like GEPHI-5SY did for filters.

Unlike writeParameter, which swallows exceptions in a catch (Exception e) block, LayoutModelPersistenceProvider.writeXML only catches XMLStreamException, so this NPE would propagate uncaught on project save. Given the PR's stated goal ("no filter property... can ever push a null into the XML writer"), it might be worth applying the same guard here too, since it's the same underlying Serialization null-propagation pattern in a sibling persistence provider.

@mbastian
mbastian merged commit 1c1a5b6 into master Aug 23, 2026
1 check passed
@mbastian mbastian added this to the 0.11.3 milestone Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant