Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 8fe86e7861 Workspace member lookup crashes on missing entity
https://sonarly.com/issue/4106?type=bug

The buildActorMetadata method in ActorFromAuthContextService uses findOneOrFail to look up a workspaceMember by userId, which throws an EntityNotFoundError when no matching workspace member exists. This blocks all create/update operations for affected users.

Fix: 1. Changed findOneOrFail to findOne in the buildActorMetadata method so it returns null instead of throwing when no workspace member is found.
2. Added a null check with a warning log when the workspace member is not found.
3. When the workspace member is missing, the fix falls back to building actor metadata from the user entity's firstName and lastName fields, allowing the operation to proceed gracefully.
4. Updated the test file to mock findOne instead of findOneOrFail to match the new behavior.
2026-02-20 14:20:03 +00:00
3 changed files with 127 additions and 9 deletions
+106
View File
@@ -0,0 +1,106 @@
# Bug #4106:
## CRITICAL: Working Directory
Your working directory is `/work/worktrees/twentyhq-twenty-wt-3002`. This is your primary repository (`twentyhq/twenty`).
All edits MUST be performed within this directory.
## Error Details
- **Error type**: `EntityNotFoundError`
- **Error message**: `Could not find any entity of type "workspaceMember" matching: {
"where": {
"userId": "aa923ee6-c763-42f8-bf2f-9a3c846923ca"
}
}`
Users are unable to access workspace member details because the system cannot find the corresponding entity. This prevents users from viewing or interacting with workspace member information.
## Severity Assessment
After analyzing the bug, determine severity independently based on your analysis:
- **critical**: User CANNOT complete a critical task. Crashes, core feature broken, data loss risk.
- **medium**: User CAN complete tasks but degraded experience. Non-critical feature broken, workaround exists.
- **low**: User unlikely to notice. Cosmetic, rare edge case, transient error that auto-recovers.
Your severity assessment in the output JSON is the FINAL severity that will be used.
## STRICT GIT RULES (violations = failed run)
- You are already on the correct fix branch — do NOT run `git checkout`, `git switch`, or `git merge`
- **NEVER run `git merge`** — merging other branches (feature branches, main, etc.) is FORBIDDEN
- **NEVER run `git checkout` on a different branch** — stay on your current branch at all times
- **NEVER run `git rebase`** — rebasing is not needed and can corrupt the worktree
- If the code you need to fix is not on this branch, fix the code that IS on this branch
or report that the fix cannot be applied — do NOT merge or pull in other branches
- **NEVER run `git add`, `git commit`, or `git push`** — the system handles all git write operations automatically after your analysis
- Only allowed git commands: `git blame`, `git log`, `git diff`, `git show` (read-only)
## Your Task (in this order)
1. **Find the Root Cause**
- Locate the exact file and line where the bug originates
- Understand why this error occurs
2. **Git Blame Analysis**
- Use `git blame` to find the commit that introduced this bug
- Note the commit SHA, author, and date
3. **Fix the Bug**
- Implement a **minimal, targeted fix** — only change what's necessary to resolve this specific error
- **NEVER modify shared/generic utilities, base classes, or ORM internals** to work around a missing field or feature.
Instead, fix the specific code paths that trigger the error (e.g. add a guard/try-catch in the caller).
Changing a generic utility to silently skip errors is ALWAYS wrong — it masks future bugs.
- Do NOT refactor surrounding code, add new features, or make unrelated improvements
- Do NOT build new components, screens, or functionality — only fix the reported error
- Add appropriate error handling if needed
- Do NOT run any git write commands (`git add`, `git commit`, `git push`) — the system handles committing and pushing automatically after your analysis
4. **Output Structured Analysis**
At the end of your response, output a JSON block between these markers:
```
SONARLY_ANALYSIS_START
{
"title": "User-focused title (max 60 chars)",
"severity": "critical|medium|low",
"summary": "1-2 sentence summary of the bug",
"user_impact": "Who was impacted and how they were impacted",
"evidence": { // Each code item: file (relative path from repo root), line (integer), snippet (faulty code, max ~10 lines). Multiple items for multiple files. {} if no evidence.
"code": [
{"file": "src/checkout/process.py", "line": 42, "snippet": "def process_order(order_id):\n orders = db.query(\"SELECT * FROM orders\") # missing WHERE clause"}
]
},
"root_cause": {
"explanation": "Root cause explanation. Use plain text only — NO bold, italic, or code backticks. When listing multiple points, use a numbered list (1. 2. 3.) with each item on its own line. Include who introduced it (git blame author), when (date), and why.",
"blame": {
"commit_sha": "abc123def (the full commit SHA from git blame, or null if unknown)",
"author": "Author Name (from git blame output, or null if unknown)",
"date": "2026-01-15 (date from git blame, or null if unknown)",
"commit_url": "https://github.com/{owner}/{repo}/commit/{sha} (build from git remote get-url origin + sha, strip any access token, or null)",
"explanation": "Brief explanation of when/who introduced the bug (e.g. Added in commit abc123 by John on Jan 15)"
}
},
"suggested_fix": {
"explanation": "What the fix does and why it solves the problem. Use plain text only — NO bold, italic, or code backticks. When listing multiple steps, use a numbered list (1. 2. 3.) with each item on its own line.",
"files_changed": ["path/to/file.py", "path/to/other.py (list of file paths you created or modified in the fix)"]
}
}
SONARLY_ANALYSIS_END
```
CRITICAL: The JSON block is the ONLY part of your response shown to the user.
Everything outside the SONARLY_ANALYSIS markers is discarded. Put ALL investigation details,
evidence (code snippets and log lines), and findings INSIDE the JSON fields,
especially root_cause.explanation, and summary. Do NOT write a separate investigation summary before the JSON.
IMPORTANT: The JSON between SONARLY_ANALYSIS_START and SONARLY_ANALYSIS_END must be valid JSON.
## Critical: ALWAYS output the analysis JSON
You MUST output SONARLY_ANALYSIS_START...SONARLY_ANALYSIS_END even if you could not find the code or fix the bug.
If you cannot locate the relevant code or create a fix,
explain what you found in the `root_cause.explanation` field.
A run without the analysis JSON is a FAILED run — never end without it.
Don't ask any questions to the user
@@ -34,7 +34,7 @@ const fromFullNameMetadataToName = ({
describe('ActorFromAuthContextService', () => {
let service: ActorFromAuthContextService;
const mockWorkspaceMemberRepository = {
findOneOrFail: jest.fn(),
findOne: jest.fn(),
};
const globalWorkspaceOrmManager: jest.Mocked<
Pick<
@@ -137,7 +137,7 @@ describe('ActorFromAuthContextService', () => {
},
} as const satisfies Partial<WorkspaceMemberWorkspaceEntity>;
mockWorkspaceMemberRepository.findOneOrFail.mockResolvedValueOnce(
mockWorkspaceMemberRepository.findOne.mockResolvedValueOnce(
mockedWorkspaceMember,
);
@@ -177,7 +177,7 @@ describe('ActorFromAuthContextService', () => {
},
} as const satisfies Partial<WorkspaceMemberWorkspaceEntity>;
mockWorkspaceMemberRepository.findOneOrFail.mockResolvedValueOnce(
mockWorkspaceMemberRepository.findOne.mockResolvedValueOnce(
mockedWorkspaceMember,
);
@@ -176,13 +176,25 @@ export class ActorFromAuthContextService {
{ shouldBypassPermissionChecks: true },
);
const workspaceMember = await workspaceMemberRepository.findOneOrFail(
{
where: {
userId: user.id,
},
const workspaceMember = await workspaceMemberRepository.findOne({
where: {
userId: user.id,
},
);
});
if (!workspaceMember) {
this.logger.warn(
`Workspace member not found for userId ${user.id} in workspace ${workspace.id}`,
);
return buildCreatedByFromFullNameMetadata({
fullNameMetadata: {
firstName: user.firstName ?? '',
lastName: user.lastName ?? '',
},
workspaceMemberId: '',
});
}
return buildCreatedByFromFullNameMetadata({
fullNameMetadata: workspaceMember.name,