different-ai/openwork · error · ApiError
invalid_payload
invalid_payload
Error message
${field} is required What it means
requireStringField is a helper in the session-groups routes that asserts a body field exists as a non-empty (after trim) string. Used for fields like "label" when renaming a group, it throws ApiError 400 code "invalid_payload" with the message "${field} is required" when the field is missing, not a string, or whitespace-only. The trimmed value is then used (e.g. sliced to 120 chars for labels).
Source
Thrown at apps/server/src/routes/session-groups.ts:51
readJsonBody,
ensureWritable,
requireClientScope,
resolveWorkspace,
resolveWorkspaceWithoutBootstrap,
} = options;
const sessionGroupEvents = new SessionGroupEventStore();
async function updateWorkspaceSessionGroups(
workspaceId: string,
updater: (current: SessionGroupState) => SessionGroupState,
) {
return updateSessionGroupState(config, workspaceId, updater);
}
function requireStringField(body: Record<string, unknown>, field: string): string {
const value = body[field];
if (typeof value !== "string" || !value.trim()) {
throw new ApiError(400, "invalid_payload", `${field} is required`);
}
return value.trim();
}
addRoute(routes, "GET", "/workspace/:id/session-groups", "client", async (ctx) => {
const workspace = await resolveWorkspaceWithoutBootstrap(config, ctx.params.id);
const result = await readSessionGroupState(config, workspace.id);
return jsonResponse({ state: result.state, updatedAt: result.updatedAt });
});
addRoute(routes, "PUT", "/workspace/:id/session-groups", "client", async (ctx) => {
ensureWritable(config);
requireClientScope(ctx, "collaborator");
const workspace = await resolveWorkspace(config, ctx.params.id);
const body = await readJsonBody(ctx.request);
const state = normalizeSessionGroupState(body.state);
const result = await updateWorkspaceSessionGroups(workspace.id, () => state);
sessionGroupEvents.record(workspace.id, "imported");View on GitHub (pinned to 2b7df46e8a)
Solutions
- Send the required field as a non-empty string, e.g. { "label": "My group" }.
- Validate on the client (non-empty after trim) before calling the endpoint; disable submit for blank input.
- Confirm the field name matches the API (label, not name or title).
Example fix
// before
await api.renameSessionGroup(id, groupId, { name: label ?? undefined });
// after
const trimmed = (label ?? "").trim();
if (!trimmed) throw new Error("Label is required");
await api.renameSessionGroup(id, groupId, { label: trimmed.slice(0, 120) }); Defensive patterns
Strategy: validation
Validate before calling
const label = typeof body.label === "string" ? body.label.trim() : "";
if (!label) throw new Error("label is required (non-empty string)"); Type guard
function isNonEmptyString(v) { return typeof v === "string" && v.trim().length > 0; } Try / catch
try {
await api.renameSessionGroup(wid, gid, { label });
} catch (e) {
if (e.code === "invalid_payload" && /label is required/.test(e.message)) {
promptUserForLabel(); // recover by asking again
} else throw e;
} Prevention
- Trim and validate user input before submit; disable submit on empty.
- Match field names exactly (label, not name/title).
- Initialize form state to "" rather than null/undefined.
When it happens
Trigger: PATCH /workspace/:id/session-groups/:groupId with body.label missing, null, a number, or " " (whitespace only); any other route using requireStringField with an absent/empty field.
Common situations: Rename dialog submitted empty; client sends {name: ...} instead of {label: ...}; JSON.stringify drops an undefined label; form state initialized to null.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/4057a2752fda0ff1.
Report an issue: GitHub.