-
Notifications
You must be signed in to change notification settings - Fork 4.7k
fix: ai settings page crash #15455
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: ai settings page crash #15455
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Greptile Overview
Greptile Summary
Fixed crash in AI settings page when no AI models are configured by adding optional chaining to SelectControl component.
- Added optional chaining (
?.) when accessingselectedOption.IconandselectedOption.label - Prevented crash when
Selectcomponent passesundefinedforselectedOption(occurs whenoptionsarray is empty and noemptyOptionprovided) - Used nullish coalescing (
??) to provide empty string fallback for label
Issue found:
- Type signature for
selectedOptionprop doesn't match runtime behavior - declares as required but can receiveundefined
Confidence Score: 3/5
- Safe to merge with one type safety issue to address post-merge
- The defensive fix resolves the immediate crash, but leaves a type mismatch between the declared interface and actual runtime behavior that should be corrected for better type safety
- No files require immediate attention, but consider updating
SelectControlPropstype definition as a follow-up
Important Files Changed
File Analysis
| Filename | Score | Overview |
|---|---|---|
| packages/twenty-front/src/modules/ui/input/components/SelectControl.tsx | 3/5 | Fixed crash by adding optional chaining, but type signature needs update to match runtime behavior |
Sequence Diagram
sequenceDiagram
participant User
participant SettingsAIAgentForm
participant useAiModelOptions
participant Select
participant SelectControl
User->>SettingsAIAgentForm: Navigate to AI settings page
SettingsAIAgentForm->>useAiModelOptions: Get AI model options
useAiModelOptions-->>SettingsAIAgentForm: Returns empty array []
SettingsAIAgentForm->>Select: Render with options=[], no emptyOption
Select->>Select: Calculate selectedOption
Note over Select: options.find() || emptyOption || options[0]<br/>= undefined || undefined || undefined<br/>= undefined
Select->>SelectControl: Pass selectedOption=undefined
alt Before Fix
SelectControl->>SelectControl: Access selectedOption.Icon
Note over SelectControl: TypeError: Cannot read properties<br/>of undefined (reading 'Icon')
SelectControl-->>User: Crash!
else After Fix
SelectControl->>SelectControl: Access selectedOption?.Icon
Note over SelectControl: Returns undefined (safe)
SelectControl->>SelectControl: Render label with selectedOption?.label ?? ''
SelectControl-->>User: Renders empty select (no crash)
end
Additional Comments (1)
-
packages/twenty-front/src/modules/ui/input/components/SelectControl.tsx, line 64 (link)syntax: Type signature claims
selectedOptionis required butSelect.tsxcan passundefinedwhenoptionsis empty and noemptyOptionprovided. Update type to reflect reality:
1 file reviewed, 1 comment
|
@charlesBochet please check this as well |
prastoin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello there thanks for your contribution, left a comment
| /> | ||
| ) : null} | ||
| <OverflowingTextWithTooltip text={selectedOption.label} /> | ||
| <OverflowingTextWithTooltip text={selectedOption?.label ?? ''} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remark: Either redundant or would need to update type for this to be optional, about to double check
prastoin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed the issue comes from the label being undefined, this fix is not enough
| ); | ||
|
|
||
| const selectedOption = | ||
| options.find(({ value: key }) => key === value) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remark: this did not handle '' or 0 atomic values correctly before same for null
prastoin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @lucasbordeau could you please have a look to this when you have some free time ?
| options.find(({ value: key }) => key === value) || | ||
| emptyOption || | ||
| options[0]; | ||
| if (fromMatchingOption !== undefined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use !isDefined()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would involve a regression,fromMatchingOption is nullable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export type SelectOption<
Value extends string | number | boolean | null = string,
> = {
Icon?: IconComponent | null;
label: string;
value: Value;
disabled?: boolean;
color?: ThemeColor | 'transparent';
};There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why ? We treat null and undefined the same in the application, we shouldn't make a difference, that's why we have isDefined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating different code paths for null and undefined is not a good idea as we have already discussed a few months back, the default way should be to use isDefined everywhere and in some very rare edge cases to differentiate between null and undefined, not the other way around.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I modified the file and applied the correct typeguards that should be used everywhere so you can see what we expect in the app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I hope that we agree on that very soon because that's how we handle nullable values since 2 years, so better not introducing and favoring another way to work with nullable now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To go in your direction, we could separate here indeed the option.value treatment between null and undefined, but this is not required here and the code that was checking for undefined wasn't checking option.value but option object, which shouldn't be treated as undefined only but nullable with isDefined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example in filteredOptions we could add a option.value !== null but using a isDefined(option.value) would also work great, I don't see a precise use case that would require handling null value separately here, using isDefined would work also.
That's the thing with undefined and null, from experience they should not be separated because very often they can indeed be treated the same and they should be from a maintenance and cognitive load perspective.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And you're right for empty option array, but that's again another use case than null | undefined.
See this issue : twentyhq/core-team-issues#1826
| return fromMatchingOption; | ||
| } | ||
|
|
||
| if (emptyOption !== undefined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same answer
lucasbordeau
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
🚀 Preview Environment Ready! Your preview environment is available at: http://bore.pub:30291 This environment will automatically shut down when the PR is closed or after 5 hours. |
|
Thanks @anadi45 for your contribution! |

Fixes - #14995