{"record":{"id":"4057a2752fda0ff1","repo":"different-ai/openwork","slug":"invalid-payload-4057a2","errorCode":"invalid_payload","errorMessage":"${field} is required","messagePattern":"(.+?) is required","errorType":"http","errorClass":"ApiError","httpStatus":400,"severity":"error","filePath":"apps/server/src/routes/session-groups.ts","lineNumber":51,"sourceCode":"    readJsonBody,\n    ensureWritable,\n    requireClientScope,\n    resolveWorkspace,\n    resolveWorkspaceWithoutBootstrap,\n  } = options;\n  const sessionGroupEvents = new SessionGroupEventStore();\n\n  async function updateWorkspaceSessionGroups(\n    workspaceId: string,\n    updater: (current: SessionGroupState) => SessionGroupState,\n  ) {\n    return updateSessionGroupState(config, workspaceId, updater);\n  }\n\n  function requireStringField(body: Record<string, unknown>, field: string): string {\n    const value = body[field];\n    if (typeof value !== \"string\" || !value.trim()) {\n      throw new ApiError(400, \"invalid_payload\", `${field} is required`);\n    }\n    return value.trim();\n  }\n\n  addRoute(routes, \"GET\", \"/workspace/:id/session-groups\", \"client\", async (ctx) => {\n    const workspace = await resolveWorkspaceWithoutBootstrap(config, ctx.params.id);\n    const result = await readSessionGroupState(config, workspace.id);\n    return jsonResponse({ state: result.state, updatedAt: result.updatedAt });\n  });\n\n  addRoute(routes, \"PUT\", \"/workspace/:id/session-groups\", \"client\", async (ctx) => {\n    ensureWritable(config);\n    requireClientScope(ctx, \"collaborator\");\n    const workspace = await resolveWorkspace(config, ctx.params.id);\n    const body = await readJsonBody(ctx.request);\n    const state = normalizeSessionGroupState(body.state);\n    const result = await updateWorkspaceSessionGroups(workspace.id, () => state);\n    sessionGroupEvents.record(workspace.id, \"imported\");","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/server/src/routes/session-groups.ts#L33-L69","documentation":"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).","triggerScenarios":"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.","commonSituations":"Rename dialog submitted empty; client sends {name: ...} instead of {label: ...}; JSON.stringify drops an undefined label; form state initialized to null.","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)."],"exampleFix":"// before\nawait api.renameSessionGroup(id, groupId, { name: label ?? undefined });\n// after\nconst trimmed = (label ?? \"\").trim();\nif (!trimmed) throw new Error(\"Label is required\");\nawait api.renameSessionGroup(id, groupId, { label: trimmed.slice(0, 120) });","handlingStrategy":"validation","validationCode":"const label = typeof body.label === \"string\" ? body.label.trim() : \"\";\nif (!label) throw new Error(\"label is required (non-empty string)\");","typeGuard":"function isNonEmptyString(v) { return typeof v === \"string\" && v.trim().length > 0; }","tryCatchPattern":"try {\n  await api.renameSessionGroup(wid, gid, { label });\n} catch (e) {\n  if (e.code === \"invalid_payload\" && /label is required/.test(e.message)) {\n    promptUserForLabel(); // recover by asking again\n  } else throw e;\n}","preventionTips":["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."],"tags":["http-400","payload-validation","required-field"],"backgroundTag":"missing-required-argument","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}