toeverything/AFFiNE · error · BadRequestException
description must be provided explicitly.
Error message
description must be provided explicitly.
What it means
Input contract check in the BYOK resolver: updating a workspace BYOK profile requires the description field to be present in the mutation input — explicitly, even when the intent is to clear it (description: null). requireExplicitDescription uses Object.hasOwn(input, 'description'), so omitting the key entirely (as many GraphQL clients do for unchanged fields) throws BadRequestException instead of silently leaving description untouched.
Source
Thrown at packages/backend/server/src/plugins/copilot/byok/resolver.ts:723
.user(userId)
.workspace(workspaceId)
.allowLocal()
.assert('Workspace.Settings.Read');
}
private async assertUpdate(userId: string, workspaceId: string) {
await this.ac
.user(userId)
.workspace(workspaceId)
.allowLocal()
.assert('Workspace.Settings.Update');
await this.entitlement.assertManagementAccess(workspaceId, userId);
}
}
function requireExplicitDescription(input: { description: string | null }) {
if (!Object.hasOwn(input, 'description')) {
throw new BadRequestException('description must be provided explicitly.');
}
}
function nativeDefinition(input: WorkspaceByokProfileDefinitionInput) {
return {
...input,
endpoint: {
...input.endpoint,
url: input.endpoint.url ?? undefined,
dialect: input.endpoint.dialect ?? undefined,
},
};
}
function projectProbe(probe: {
kind: string;
testedAtMs?: number;
errorKind?: string;View on GitHub (pinned to b4c8548c09)
Solutions
- Always include description in the update input — pass its current value to keep it, or null to clear it.
- If your GraphQL client prunes nulls, configure it to preserve explicit nulls for this mutation (e.g. omitNull: false in apollo-client v3 composites) or build the input literal manually.
- On the server, consider splitting into explicit update paths if clients cannot express 'field absent' vs 'field null'.
Example fix
# before
mutation { updateByokProfile(workspaceId: "ws", input: { name: "gpt4" }) }
# after — description present; null clears it explicitly
mutation { updateByokProfile(workspaceId: "ws", input: { name: "gpt4", description: null }) } Defensive patterns
Strategy: validation
Validate before calling
// Explicitly control field presence before sending the update
const input: WorkspaceByokProfileDefinitionInput & { description: string | null } = {
...patch,
description: 'description' in patch ? patch.description : currentProfile.description,
}; Type guard
const hasExplicitDescription = (
input: object
): input is { description: string | null } =>
Object.hasOwn(input, 'description'); Try / catch
try {
await updateByokProfile({ workspaceId, input });
} catch (e) {
if (/description must be provided explicitly/i.test(String(e?.message))) {
return updateByokProfile({ workspaceId, input: { ...input, description: input.description ?? null } });
}
throw e;
} Prevention
- Always include description in update inputs — current value to keep, null to clear.
- Configure the GraphQL client to preserve explicit nulls instead of pruning them.
- Write a client-side hasExplicitDescription check before submitting BYOK updates.
When it happens
Trigger: Calling updateByokProfile (or equivalent) with the description field omitted; GraphQL clients that strip null/unset fields before sending; code paths building the input via spread of a partial object where description was never set; auto-generated clients dropping optional fields.
Common situations: Frontend forms that only send changed fields; Apollo/urql normalized updates pruning nulls; scripts PATCHing only the fields they modify; client generated from schema marking description optional and omitting it.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/068e1891069d1028.
Report an issue: GitHub.