hcengineering/platform · error · PlatformError
account.status.Forbidden
account.status.Forbidden
Error message
Forbidden
What it means
Thrown when removing a social id from an account would leave it in an invalid state. After computing the social ids that would remain ('afterRemoval'), the account service refuses the release if no HULY social id remains or if no login-capable social id remains. This guarantees every account keeps a working login method.
Source
Thrown at server/account/src/operations.ts:3014
}
}
if (personUuid == null) {
throw new PlatformError(new Status(Severity.ERROR, platform.status.BadRequest, {}))
}
if (!allowedService) {
// User should always have at least one Huly and one "login" social id
// so do not allow releasing last ones
const socialIds = await db.socialId.find({ personUuid, verifiedOn: { $gt: 0 }, isDeleted: { $ne: true } })
const afterRemoval = socialIds.filter((it) => it.type !== type || it.value !== value)
if (afterRemoval.filter((it) => it.type === SocialIdType.HULY).length === 0) {
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
}
if (afterRemoval.filter((it) => loginSocialTypes.includes(it.type)).length === 0) {
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
}
}
return await doReleaseSocialId(db, personUuid, type, value, extra?.service ?? account, deleteIntegrations)
}
export async function deleteAccount (
ctx: MeasureContext,
db: AccountDB,
branding: Branding | null,
token: string,
params: { uuid?: AccountUuid }
): Promise<void> {
const { extra } = decodeTokenVerbose(ctx, token)
const isAdmin = extra?.admin === 'true'
if (!isAdmin) {View on GitHub (pinned to 63e28dc964)
Solutions
- Add a new login-capable social id to the account before removing the current one
- Remove a different (non-last) social id instead
- If the removal is intentional, first release other ids then re-add a valid login id immediately after in the same administrative flow
Example fix
// before await client.removeSocialId(token, SocialIdType.GITHUB, value) // account has only this login id // after await client.addSocialId(token, SocialIdType.EMAIL, newEmail) await client.removeSocialId(token, SocialIdType.GITHUB, value)
Defensive patterns
Strategy: validation
Validate before calling
const remaining = socialIds.filter(id => id.value !== removingValue)
const loginTypes = [SocialIdType.HULY, SocialIdType.EMAIL, SocialIdType.GITHUB]
if (remaining.filter(i => i.type === SocialIdType.HULY).length === 0 ||
remaining.filter(i => loginTypes.includes(i.type)).length === 0) {
throw new Error('cannot remove: account must keep a HULY id and at least one login id')
} Type guard
function hasSafeRemaining(ids: SocialId[], removing: SocialId): boolean {
const rest = ids.filter(i => i._id !== removing._id)
return rest.some(i => i.type === SocialIdType.HULY) && rest.some(i => LOGIN_TYPES.includes(i.type))
} Try / catch
try {
await accountClient.releaseSocialId(token, type, value)
} catch (err) {
if ((err as PlatformError).props?.code === platform.status.Forbidden) {
console.error('cannot remove last login social id; add a replacement first')
} else throw err
} Prevention
- Always add a replacement social id before removing the current login id
- Query the account's social ids first and compute the post-removal set
- Block removal of HULY-type ids in UI for accounts with only one
When it happens
Trigger: Calling the release/remove social id operation (doReleaseSocialId path in server/account/src/operations.ts) when it would remove the last HULY-type social id, or the last social id whose type is in the allowed loginSocialTypes list.
Common situations: A user tries to unlink their only Huly login or last email/GitHub identity; scripts cleaning up social integrations delete all ids at once; admin tooling removes a social id without first adding a replacement.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- AccountNotFound
- SocialIdNotFound
- SocialIdNotFound
- Social ID is missing
- Workspace ${options.workspace} not found
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/f7487e4d781b5615.
Report an issue: GitHub.