{"record":{"id":"95bc8bee0bb2515e","repo":"Freika/dawarich","slug":"familylayer-invalid-locations-data","errorCode":null,"errorMessage":"[FamilyLayer] Invalid locations data:","messagePattern":"\\[FamilyLayer\\] Invalid locations data:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"app/javascript/maps_maplibre/layers/family_layer.js","lineNumber":335,"sourceCode":"   * Remove family member\n   */\n  removeMember(memberId) {\n    const features = this.data?.features || []\n    const filtered = features.filter((f) => f.properties.id !== memberId)\n\n    this.update({\n      type: \"FeatureCollection\",\n      features: filtered,\n    })\n  }\n\n  /**\n   * Load all family members from API\n   * @param {Object} locations - Array of family member locations\n   */\n  loadMembers(locations) {\n    if (!Array.isArray(locations)) {\n      console.warn(\"[FamilyLayer] Invalid locations data:\", locations)\n      return\n    }\n\n    const features = locations.map((location) => ({\n      type: \"Feature\",\n      geometry: {\n        type: \"Point\",\n        coordinates: [location.longitude, location.latitude],\n      },\n      properties: {\n        id: location.user_id,\n        name: location.email || translate(\"common.unknown\"),\n        email: location.email,\n        color: location.color || this.getMemberColor(location.user_id),\n        lastUpdate: Date.now(),\n        battery: location.battery,\n        batteryStatus: location.battery_status,\n        updatedAt: location.updated_at,","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/Freika/dawarich/blob/97fad417c5a11b0eb11157890635e015723a2e97/app/javascript/maps_maplibre/layers/family_layer.js#L317-L353","documentation":"FamilyLayer.loadMembers requires an array of member-location objects and warns when handed anything else. The coordinates are then used unconditionally (location.longitude, location.latitude), so passing an error object, null, or a wrapped payload would produce garbage features — hence the early guard. The warn prints the offending value, which tells you exactly what shape arrived instead of the expected array.","triggerScenarios":"The family locations API returned an error envelope like { \"error\": \"...\" } or null (family tracking disabled, unauthorized, member has no family), or the caller passed response.data / response JSON wrapper instead of the embedded array (e.g. response.locations).","commonSituations":"Fetching family locations while family tracking is disabled so the endpoint returns an error hash; API version change wrapping the array in an envelope; ActionCable broadcast delivering an object rather than an array; off-by-one unwrap in the fetch layer.","solutions":["Log/inspect the value passed to loadMembers and unwrap the correct key from the API response (e.g. data.locations or data.family_members) before calling it.","Handle non-200 responses or error envelopes in the fetching code and skip loadMembers entirely when the payload is not the expected array.","If family tracking can legitimately be off, treat null as \"clear the layer\" instead of feeding it to loadMembers."],"exampleFix":"// before\nfamilyLayer.loadMembers(data)\n\n// after\nconst locations = Array.isArray(data) ? data : data?.locations\nif (Array.isArray(locations)) {\n  familyLayer.loadMembers(locations)\n}","handlingStrategy":"type-guard","validationCode":"if (!Array.isArray(locations)) return","typeGuard":"const isMemberLocations = (d) =>\n  Array.isArray(d) && d.every((m) => m && \"longitude\" in m && \"latitude\" in m)","tryCatchPattern":null,"preventionTips":["Unwrap API envelopes explicitly and assert Array.isArray before rendering.","Treat non-array payloads (error objects, null) as a clear-layer or no-op signal, never as data."],"tags":["maplibre","api-response-shape","family-sharing","type-validation"],"backgroundTag":"api-response-shape-mismatch","analyzedSha":"97fad417c5a11b0eb11157890635e015723a2e97","analyzedAt":"2026-08-21T17:04:17.778Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}