hcengineering/platform · warning
Missing or invalid field: plan
Error message
Missing or invalid field: plan
What it means
HTTP 400 returned by POST /api/v1/subscriptions/:subscriptionId/updatePlan when req.body.plan is missing or not a string (services/payment/pod-payment/src/server.ts:397-400). The handler destructures `{ plan }` from the JSON body and requires a string plan name such as 'common', 'rare', 'epic', or 'legendary'. This is input validation rejecting the request before any provider or DB calls.
Source
Thrown at services/payment/pod-payment/src/server.ts:398
withToken,
withLoginInfo,
withOwner,
(req: RequestWithAuth, res: Response) => {
if (provider === undefined) {
res.status(503).json({ error: 'Payment provider is not configured' })
return
}
void handleRequest(
ctx,
'update-plan',
async (ctx) => {
const subscriptionId = req.params.subscriptionId
const { plan } = req.body
const loginInfo = req.loginInfo as WorkspaceLoginInfo
if (plan === undefined || typeof plan !== 'string') {
res.status(400).json({ error: 'Missing or invalid field: plan' })
return
}
if (loginInfo?.workspaceUrl === undefined) {
res.status(401).json({ error: 'Missing workspace url in login info' })
return
}
// Get subscription from our database using internal ID
const subscription = await accountClient.getSubscriptionById(subscriptionId)
if (subscription === undefined || subscription === null) {
res.status(404).json({ error: 'Subscription not found' })
return
}
const accountUuid = subscription.accountUuid ?? req.token?.account
if (accountUuid == null) {View on GitHub (pinned to 63e28dc964)
Solutions
- Send a JSON body containing a string plan: { "plan": "pro" } with Content-Type: application/json.
- Validate the plan field on the client before calling (typeof plan === 'string').
- Confirm the plan name matches one of the valid plan identifiers ('common', 'rare', 'epic', 'legendary').
- If using a fetch/axios wrapper, ensure JSON.stringify is used and the content-type header is set.
Example fix
// before
await fetch(url, { method: 'POST', body: { plan: 2 } })
// after
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ plan: 'epic' })
}) Defensive patterns
Strategy: validation
Validate before calling
function validateUpdatePlanBody(body: unknown): { plan: string } {
const { plan } = (body ?? {}) as { plan?: unknown }
if (typeof plan !== 'string' || plan.length === 0) throw new ValidationError('plan must be a non-empty string')
return { plan }
} Type guard
function hasValidPlan(body: unknown): body is { plan: string } {
return typeof body === 'object' && body !== null && 'plan' in body && typeof (body as { plan: unknown }).plan === 'string'
} Try / catch
try {
const res = await fetch(`${base}/api/v1/subscriptions/${id}/updatePlan`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...auth },
body: JSON.stringify({ plan })
})
if (res.status === 400) {
const { error } = await res.json()
throw new ClientError(error ?? 'invalid plan field')
}
return await res.json()
} catch (err) {
logger.warn({ err }, 'updatePlan rejected')
throw err
} Prevention
- Always send JSON with Content-Type: application/json.
- Validate request bodies with a schema (zod/ajv) on both client and server.
- Keep a single shared constant list of valid plan names.
- Add client-side unit tests for the plan-update payload.
When it happens
Trigger: POST .../updatePlan with no JSON body, body without a `plan` key, plan sent as a non-string (number, object, null), or request missing Content-Type: application/json so Express leaves body.plan undefined.
Common situations: Client forgot the JSON body or used form encoding; plan identifier renamed/typo'd on the client; sending plan id (number) instead of plan name (string); middleware/json parsing not configured on a custom client.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Missing required parameters
- Missing or invalid required parameter: _class
- Missing required fields: type, plan
- Failed to load server config
- getDisplayMedia not supported
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/1b1b23e791ed0d5e.
Report an issue: GitHub.