Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/frontend/src/components/launches/calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,7 @@ const CalendarItem: FC<{
<div
className="absolute -top-[6px] -left-[6px] z-20 w-[18px] h-[18px] rounded-full bg-red-500 flex items-center justify-center text-white text-[11px] font-bold cursor-pointer"
data-tooltip-id="tooltip"
data-tooltip-class-name="!max-w-[400px] break-words"
data-tooltip-content={post.error || 'An error occurred while publishing this post'}
>
!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,32 @@ export class PostsRepository {
});
}

// Post.error persists the raw serialized workflow failure; only curated
// provider messages are surfaced to the calendar and the public API —
// bad_body failures whose message came from a provider handleErrors
// mapping or a hand-written provider throw (unmapped API responses carry
// the 'Unknown Error' placeholder instead). Internal debug strings (e.g.
// 'Refresh channel needed') and unmapped failures come out as null.
private curatedError(error?: string | null): string | null {
if (!error) {
return null;
}
try {
const parsed = JSON.parse(error);
const [type, message] = parsed?.cause?.failure?.message
? [parsed?.cause?.type, parsed?.cause?.failure?.message]
: [
parsed?.failure?.cause?.applicationFailureInfo?.type,
parsed?.failure?.cause?.message,
];
return type === 'bad_body' && message && message !== 'Unknown Error'
Comment on lines +143 to +148

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The curatedError function uses inconsistent paths to extract the error type and message from nested objects, which will likely cause type checks like type === 'bad_body' to fail.
Severity: MEDIUM

Suggested Fix

Align the data extraction paths in the curatedError function. When parsing the error, ensure the type is extracted from the same nested ApplicationFailure object as the message. For example, use a path like parsed?.cause?.failure?.applicationFailureInfo?.type to retrieve the type, consistent with how other parts of the function handle nested failures.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location:
libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts#L143-L148

Potential issue: In the `curatedError` function, there is an inconsistent data
extraction path for handling nested errors from Temporal workflows. When
`parsed?.cause?.failure?.message` exists, the code extracts the error `type` from
`parsed?.cause?.type` but the `message` from the more deeply nested
`parsed?.cause?.failure?.message`. This creates a mismatch, as the `type` will likely
belong to a wrapper error (e.g., `ActivityFailure`) rather than the intended
`ApplicationFailure` (e.g., `bad_body`). As a result, the check `type === 'bad_body'`
will fail, preventing specific errors like `BadBody` from being correctly identified and
handled.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not a bug. curatedError parses the serialized failure stored in Post.error, not a live ActivityFailure, and in that serialized shape type sits at cause.type as a sibling of cause.failure (visible in existing production rows). This exact path was e2e-verified on this branch: a real mapped Pinterest failure and a legacy blob both returned their curated message through this check, and a crash-shaped blob returned null. The suggested cause.failure.applicationFailureInfo.type also exists in the blob, but the current path is the verified one.

? message
: null;
} catch (err) {
return null;
}
}

async getPosts(orgId: string, query: GetPostsDto) {
// Use the provided start and end dates directly
const startDate = dayjs.utc(query.startDate).toDate();
Expand Down Expand Up @@ -173,6 +199,7 @@ export class PostsRepository {
releaseURL: true,
releaseId: true,
state: true,
error: true,
intervalInDays: true,
group: true,
creationMethod: true,
Expand All @@ -193,7 +220,9 @@ export class PostsRepository {
},
});

return list.reduce((all, post) => {
return list
.map((post) => ({ ...post, error: this.curatedError(post.error) }))
.reduce((all, post) => {
if (!post.intervalInDays) {
return [...all, post];
}
Expand Down
10 changes: 6 additions & 4 deletions libraries/nestjs-libraries/src/integrations/social.abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,13 @@ export abstract class SocialAbstract {
pendingData: any,
integration: Integration
): Promise<PendingCheckResponse> {
// 'Unknown Error' keeps this developer guard out of the user-facing
// post.error (changeState only surfaces curated bad_body messages)
throw new BadBody(
this.identifier,
'{"error":"checkPostStatus is not implemented for this provider"}',
'{}',
'{}',
'checkPostStatus is not implemented for this provider'
'Unknown Error'
);
}

Expand All @@ -231,9 +233,9 @@ export abstract class SocialAbstract {
): Promise<PendingCheckResponse> {
throw new BadBody(
this.identifier,
'{"error":"finalizePost is not implemented for this provider"}',
'{}',
'{}',
'finalizePost is not implemented for this provider'
'Unknown Error'
);
}

Expand Down
Loading