{"record":{"id":"9eeeab7f9f065537","repo":"plandex-ai/plandex","slug":"error-unmarshalling-request-9eeeab","errorCode":null,"errorMessage":"Error unmarshalling request: ","messagePattern":"Error unmarshalling request: ","errorType":"http","errorClass":null,"httpStatus":500,"severity":"warning","filePath":"app/server/handlers/invites.go","lineNumber":60,"sourceCode":"\t}\n\n\tif org.IsTrial {\n\t\twriteApiError(w, shared.ApiError{\n\t\t\tType:   shared.ApiErrorTypeTrialActionNotAllowed,\n\t\t\tStatus: http.StatusForbidden,\n\t\t\tMsg:    \"Trial user can't invite other users\",\n\t\t})\n\n\t\treturn\n\t}\n\n\tcurrentUserId := auth.User.Id\n\n\tvar req shared.InviteRequest\n\terr = json.NewDecoder(r.Body).Decode(&req)\n\tif err != nil {\n\t\tlog.Printf(\"Error unmarshalling request: %v\\n\", err)\n\t\thttp.Error(w, \"Error unmarshalling request: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\treq.Email = strings.ToLower(req.Email)\n\n\t// ensure current user can invite target user\n\tpermission := shared.Permission(strings.Join([]string{string(shared.PermissionInviteUser), req.OrgRoleId}, \"|\"))\n\n\tif !auth.HasPermission(permission) {\n\t\tlog.Printf(\"User does not have permission to invite user with role: %v\\n\", req.OrgRoleId)\n\t\thttp.Error(w, \"User does not have permission to invite user with role: \"+req.OrgRoleId, http.StatusForbidden)\n\t\treturn\n\t}\n\n\t// ensure user doesn't already have access to org via domain\n\tsplit := strings.Split(req.Email, \"@\")\n\tif len(split) != 2 {\n\t\tlog.Printf(\"Invalid email: %v\\n\", req.Email)\n\t\thttp.Error(w, \"Invalid email: \"+req.Email, http.StatusBadRequest)","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/invites.go#L42-L78","documentation":"InviteUserHandler decodes the request body into shared.InviteRequest. When json.Decoder rejects the body, the handler responds 500 (note: arguably should be 400) with 'Error unmarshalling request: <err>'. It means the payload was not valid JSON or did not match InviteRequest's field types.","triggerScenarios":"POST to the invite endpoint with an empty body, malformed JSON, email/orgRoleId/name as wrong JSON types (e.g. numbers instead of strings), or a body not matching the InviteRequest schema.","commonSituations":"Missing Content-Type combined with a form-encoded body; hand-written curl payloads with syntax errors; a CLI/SDK version whose InviteRequest JSON no longer matches the server's; a proxy mangling the body.","solutions":["Send a valid JSON body matching InviteRequest, e.g. {\"email\":\"user@example.com\",\"orgRoleId\":\"<role-id>\",\"name\":\"...\"} — validate with jq first","Check the %v detail in the response; 'cannot unmarshal X into type string' names the mismatched field","Set Content-Type: application/json and ensure the CLI/SDK version matches the server version","If you control the server, consider changing this response to 400 Bad Request, since bad input is the usual cause"],"exampleFix":"// before\ncurl -X POST $URL -d 'email=foo@bar.com&role=admin'\n// after\ncurl -X POST $URL -H 'Content-Type: application/json' -d '{\"email\":\"foo@bar.com\",\"orgRoleId\":\"admin\"}'","handlingStrategy":"validation","validationCode":"// Build and validate the InviteRequest body before sending\nreq := map[string]string{\"email\": email, \"orgRoleId\": roleID, \"name\": name}\nif email == \"\" || roleID == \"\" {\n    return errors.New(\"email and orgRoleId are required\")\n}\nbody, err := json.Marshal(req)\nif err != nil {\n    return fmt.Errorf(\"invalid invite payload: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"res, err := client.InviteUser(ctx, email, roleID)\nif err != nil && strings.Contains(err.Error(), \"Error unmarshalling request\") {\n    return fmt.Errorf(\"invite payload rejected — send JSON {email, orgRoleId} with correct types: %w\", err)\n}","preventionTips":["Always POST application/json with fields matching shared.InviteRequest","Pass email/orgRoleId as JSON strings, never numbers or booleans","Validate payloads with jq before calling the endpoint","Pin matching CLI/SDK and server versions"],"tags":["http","json","request-validation","go"],"backgroundTag":"request-body-json-decode-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}