Skip to content

fix: skip null/empty DisplayName in chat @-mention suggestions#8666

Open
biotech77 wants to merge 3 commits into
devfrom
fix/8056-profile-exception
Open

fix: skip null/empty DisplayName in chat @-mention suggestions#8666
biotech77 wants to merge 3 commits into
devfrom
fix/8056-profile-exception

Conversation

@biotech77
Copy link
Copy Markdown
Contributor

Pull Request Description

Closes #8056

What does this PR change?

SuggestionPanelChatInputState.UpdateProfileNameMap iterates the participant
list and uses Profile.CompactInfo.DisplayName as a Dictionary<string, V> key.
When DisplayName is null, TryGetValue / TryAdd throw
ArgumentNullException ("Value cannot be null. Parameter name: key") and
crash the chat input on the next keystroke or focus loss.

This PR adds a one-line guard that skips participants whose DisplayName
is null or empty — they cannot be @-mentioned anyway.

Quick reference

Run this PR (with cache)

metaforge explorer run

Run this PR (without cache — clears Explorer data)

metaforge explorer run --clear

Run this PR (fresh account — clears everything and creates a new account)

metaforge account create --clear
metaforge explorer run

Tail logs while testing

metaforge explorer logs tail --filter ""

Run automation tests against this PR

metaforge explorer test
-->

Steps (standard run):

metaforge explorer run XXXX  # ← replace with this PR number

Expected result:

Steps (fresh account):

metaforge account create --clear
metaforge explorer run XXXX  # ← replace with this PR number

Expected result:

Automation (if applicable):

metaforge explorer test XXXX

Test Steps

  1. Start mentioning people
  2. Notice error doesn't appear
Parameter name: key
  File "C:/workspace/workspace/p/Explorer/Assets/DCL/Chat/_Refactor/ChatInput/States/SuggestionPanelChatInputState.cs", line 151, in UpdateProfileNameMap
  File "C:/workspace/workspace/p/Explorer/Assets/DCL/Chat/_Refactor/ChatInput/States/SuggestionPanelChatInputState.cs", line 84, in TryFindMatchAsync
  File "C:/workspace/workspace/p/Explorer/Assets/DCL/Chat/_Refactor/ChatInput/States/SuggestionPanelChatInputState.cs", line 65, in Dispose
  File "C:/workspace/workspace/p/Explorer/Assets/DCL/Chat/_Refactor/ChatInput/States/TypingEnabledChatInputState.cs", line 136, in MoveNext
  File "C:/workspace/workspace/p/Explorer/Assets/DCL/Chat/_Refactor/ChatInput/States/TypingEnabledChatInputState.cs", line 242, in OnInputDeselected

Additional Testing Notes

  • Note any edge cases to verify
  • Mention specific areas that need careful testing
  • List known limitations or potential issues

Quality Checklist

  • Changes have been tested locally
  • Documentation has been updated (if required)
  • Performance impact has been considered
  • For SDK features: Test scene is included

Code Review Reference

Please review our Branch & PR Standards before submitting. It explains the automated review flow, QA/DEV approval requirements, and what each label does — especially useful for first-time contributors.

@biotech77 biotech77 requested review from a team as code owners May 7, 2026 10:47
@github-actions github-actions Bot requested a review from anicalbano May 7, 2026 10:47
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 7, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

@biotech77 biotech77 self-assigned this May 7, 2026
@biotech77 biotech77 added the clean-build Used to trigger clean build on PR label May 7, 2026
@biotech77
Copy link
Copy Markdown
Contributor Author

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 7, 2026

Claude finished @biotech77's task in 2m 2s —— View job


Claude Review

  • Read CLAUDE.md and docs map
  • Read changed file and analyze diff vs origin/dev
  • Root-cause analysis
  • Post review

Summary

Problem: SuggestionPanelChatInputState.UpdateProfileNameMap uses profile.DisplayName as a Dictionary<string, …> key (line 154/157/159). When a participant's CompactInfo.DisplayName is null, TryGetValue / indexer / TryAdd throw ArgumentNullException, crashing the chat input on focus loss / next keystroke.

Does the fix address the cause? It addresses the consumer-side cause — using a possibly-null string as a dictionary key. It does not investigate why CompactInfo.DisplayName ends up null in the first place (GenerateAndValidateName() always assigns at least string.Empty, so a null implies the struct was produced via a path that bypasses the constructor — e.g. default(CompactInfo) or some external mutation). However, the chosen behavior — skip participants who cannot be @-mentioned — is semantically correct on its own merit (no DisplayName ⇒ nothing to show in autocomplete), so this isn't a band-aid that swallows a real bug. Acceptable as a fix.

Blocking issues

None.

Notes (non-blocking, not required)

  • The deeper question of how CompactInfo.DisplayName becomes null is worth a follow-up at the producer side (Profile.CompactInfo construction / cache population), but isn't required for this hotfix.

REVIEW_RESULT: PASS ✅
COMPLEXITY: SIMPLE
COMPLEXITY_REASON: Three-line defensive null/empty guard in a single chat input state file; touches no ECS, async, plugin, or shared infrastructure.
QA_REQUIRED: YES
· Branch

@biotech77
Copy link
Copy Markdown
Contributor Author

@claude review

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 7, 2026

Claude finished @biotech77's task in 1m 20s —— View job


Claude Review

  • Read CLAUDE.md and docs map
  • Read changed file and analyze diff vs origin/dev
  • Root-cause analysis
  • Post review

Summary

Problem: SuggestionPanelChatInputState.UpdateProfileNameMap uses profile.DisplayName as a Dictionary<string, …> key (lines 154/157/159). When CompactInfo.DisplayName is null, TryGetValue / indexer / TryAdd throw ArgumentNullException, crashing the chat input on focus loss / next keystroke (per the stacktrace in the PR body).

Does the fix address the cause or a symptom? It fixes the consumer-side cause — guarding against a null dictionary key. The producer side (Profile.CompactInfo.Compact.DisplayName in Explorer/Assets/DCL/Profiles/SharedAPI/Profile.CompactInfo.cs) is an auto-property { get; private set; } with no initializer, so it's legitimately null until GenerateAndValidateName() runs (and the early-return at L211 can leave it as string.Empty). Skipping participants without a DisplayName is also semantically correct — they cannot be @-mentioned in the autocomplete UI. So this is a real fix, not a band-aid that swallows a bug. Acceptable.

Blocking issues

None.

Notes (non-blocking, not required)

  • Hardening Profile.CompactInfo.Compact.DisplayName to be initialized to string.Empty at the producer (matching what GenerateAndValidateName resets it to) would prevent any future consumer from hitting the same trap. Worth a follow-up but not required for this hotfix.

REVIEW_RESULT: PASS ✅
COMPLEXITY: SIMPLE
COMPLEXITY_REASON: Three-line defensive null/empty guard in a single chat input state file; touches no ECS, async, plugin, or shared infrastructure.
QA_REQUIRED: YES
· Branch

@biotech77 biotech77 removed the clean-build Used to trigger clean build on PR label May 7, 2026
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 7, 2026

Claude finished @biotech77's task in 53s —— View job


Claude Review

  • Read CLAUDE.md and docs map
  • Read changed file and analyze diff vs origin/dev
  • Root-cause analysis
  • Post review

Summary

Problem: SuggestionPanelChatInputState.UpdateProfileNameMap uses profile.DisplayName as a Dictionary<string, …> key (lines 154/157/159). When CompactInfo.DisplayName is null, TryGetValue / indexer / TryAdd throw ArgumentNullException, crashing the chat input on focus loss / next keystroke (per the stacktrace in the PR body).

Does the fix address the cause or a symptom? It fixes the consumer-side cause — guarding against a null dictionary key. The producer side (Profile.CompactInfo.Compact.DisplayName in Explorer/Assets/DCL/Profiles/SharedAPI/Profile.CompactInfo.cs) is an auto-property { get; private set; } with no initializer, so it can legitimately be null for some construction paths. Skipping participants without a DisplayName is also semantically correct — they cannot be @-mentioned in the autocomplete UI. So this is a real fix, not a band-aid that swallows a bug. Acceptable.

Blocking issues

None.

Notes (non-blocking, not required)

  • Hardening Profile.CompactInfo.Compact.DisplayName to default to string.Empty at the producer (matching what GenerateAndValidateName resets it to) would prevent any future consumer from hitting the same trap. Worth a follow-up but not required for this hotfix.

REVIEW_RESULT: PASS ✅
COMPLEXITY: SIMPLE
COMPLEXITY_REASON: Three-line defensive null/empty guard in a single chat input state file; touches no ECS, async, plugin, or shared infrastructure.
QA_REQUIRED: YES
· Branch ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-approved by Claude — simple fix/chore with no blocking issues. QA approval is still required.

Copy link
Copy Markdown
Contributor

@NickKhalow NickKhalow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explorer/Assets/DCL/Profiles/SharedAPI/Profile.CompactInfo.cs violates the contract:

/// <summary>
///     The name of the user after passing character validation, including the
///     Wallet Id (if the user doesnt have a claimed name)
/// </summary>
public string DisplayName { get; private set; }

Display name is never supposed to be null. The patch fixes consequences rather than the root cause. Please address the root cause

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 10, 2026

Claude finished @biotech77's task in 1m 38s —— View job


Claude Review

  • Read CLAUDE.md and docs map
  • Read changed file and analyze diff vs origin/dev
  • Examine producer-side (Profile.CompactInfo) to confirm root cause
  • Cross-reference with reviewer feedback (NickKhalow)
  • Post review

Summary

Problem: SuggestionPanelChatInputState.UpdateProfileNameMap crashes with ArgumentNullException because Profile.CompactInfo.DisplayName can be null, despite being used as a Dictionary<string, …> key.

Does the fix address the cause or a symptom? Symptom only. I'm aligning with @NickKhalow's review here — the prior auto-review I posted was too lenient.

The XML doc on Profile.CompactInfo.DisplayName (Profile.CompactInfo.cs:152-156) is a contract: "The name of the user after passing character validation, including the Wallet Id (if the user doesn't have a claimed name)". Nothing in that contract permits null. GenerateAndValidateName() always assigns at least string.Empty (Profile.CompactInfo.cs:190). So a null DisplayName reaching a consumer means the producer was bypassed — which is a real bug, not a defensive-coding gap in the consumer.

How does it become null? The struct's DisplayName is { get; private set; } with no initializer (line 156). Multiple construction paths skip GenerateAndValidateName() entirely:

  • Profile.cs:74new Profile()compact = new CompactInfo(); (parameterless, no init)
  • Profile.cs:106Profile.Clear()compact = new CompactInfo(); (parameterless, no init)
  • MembersListView.cs:229, EventsStateService.cs:119, PlacesStateService.cs:116, IProfileCache.cs:32default(Profile.CompactInfo) (zeroed struct, DisplayName == null)

Any of these flows can deliver a CompactInfo whose DisplayName is null to the chat input. Patching only SuggestionPanelChatInputState leaves every other consumer that uses DisplayName (sorting, search, name-color hashing, UI bindings, etc.) exposed to the same trap.

Blocking issues

1. Fix the root cause, not the consumerExplorer/Assets/DCL/Profiles/SharedAPI/Profile.CompactInfo.cs:156 (and ValidatedName at L145, MentionName at L176, UnclaimedName at L158)

  • Problem: These auto-properties have no initializer, and several constructor paths (new CompactInfo(), default(CompactInfo), Profile.Clear()) leave them null, violating the documented contract that they always hold a validated string.
  • Fix (option A, minimal & safe): Make the getters null-coalesce to string.Empty. Survives default(CompactInfo) too:
    private string? displayName;
    public string DisplayName { get => displayName ?? string.Empty; private set => displayName = value; }
    Apply the same to ValidatedName, MentionName, and UnclaimedName.
  • Fix (option B): Initialize the backing fields in every CompactInfo constructor path AND ensure Profile.cs:74 / Profile.cs:106 use a constructor that runs GenerateAndValidateName() (with an empty userId/name → still results in string.Empty). Does NOT cover the default(CompactInfo) callsites — those would also need to be audited.
  • Why: Per CLAUDE.md §1 "Component Design Principles" and the producer-side contract, consumers should not need to null-check values whose contract forbids null. The current PR fixes one consumer; the producer remains broken and the next consumer will hit the same crash.

After fixing the producer, the consumer guard added in this PR becomes unnecessary and should be reverted.

Fix this →

Notes (non-blocking)

  • The current 3-line guard is behaviorally harmless and unblocks the specific crash in the stacktrace. If the team chooses to ship this as a hotfix and address the producer in a follow-up, please open a tracking issue so the other consumers (sorting/search/UI/etc.) aren't left exposed to the same bug. But strictly per the review rubric, fixing a value at the consumer that shouldn't be null per its contract is the textbook FAIL case.

REVIEW_RESULT: FAIL ❌
COMPLEXITY: SIMPLE
COMPLEXITY_REASON: Three-line defensive guard in a single chat input state file; touches no ECS, async, plugin, or shared infrastructure — the correct fix at the producer is also small.
QA_REQUIRED: YES

Branch ·

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 11, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

@mikhail-dcl
Copy link
Copy Markdown
Collaborator

I propose to close this PR as it doesn't bring any value per se

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.

System.ArgumentNullException: Value cannot be null.

3 participants