{"record":{"id":"39965d89d5e79dde","repo":"immich-app/immich","slug":"cast-session-is-not-established","errorCode":null,"errorMessage":"Cast session is not established","messagePattern":"Cast session is not established","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"mobile/lib/repositories/gcast.repository.dart","lineNumber":59,"sourceCode":"\n    sendMessage(CastSession.kNamespaceReceiver, {'type': \"STOP\", \"sessionId\": sessionID});\n\n    // wait 500ms to ensure the stop command is processed\n    await Future.delayed(const Duration(milliseconds: 500));\n\n    await _castSession?.close();\n  }\n\n  String? getSessionId() {\n    if (_receiverStatus == null) {\n      return null;\n    }\n    return _receiverStatus!['status']['applications'][0]['sessionId'];\n  }\n\n  void sendMessage(String namespace, Map<String, dynamic> message) {\n    if (_castSession == null) {\n      throw Exception(\"Cast session is not established\");\n    }\n\n    _castSession!.sendMessage(namespace, message);\n  }\n\n  Future<List<CastDevice>> listDestinations() async {\n    return await CastDiscoveryService().search(timeout: const Duration(seconds: 3));\n  }\n}\n","sourceCodeStart":41,"sourceCodeEnd":69,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/mobile/lib/repositories/gcast.repository.dart#L41-L69","documentation":"GcastRepository.sendMessage forwards a message to the connected Google Cast session. If no session has been established (connectToDestination not yet called or the session was lost), _castSession is null and the method throws 'Cast session is not established' rather than dereferencing null or silently dropping the message.","triggerScenarios":"Calling sendMessage(namespace, message) before a successful connect/establishCastSession, after the receiver disconnects (session closed/expired), or after app resume when the cached session was invalidated.","commonSituations":"User taps a cast action while the device picker/connection flow never completed; Cast device drops off the network mid-session; UI state still shows 'casting' after session teardown so buttons remain enabled.","solutions":["Check that a cast session exists before sending: expose a getter (e.g. isSessionEstablished) or track connection state and disable send controls when unconnected.","Call the connect/establish-session method and await its completion before invoking sendMessage.","Handle Cast session lifecycle events (suspended/ended) to reset UI state and force a reconnect on send failure.","Wrap sendMessage in try-catch and on failure prompt the user to reconnect to the cast device."],"exampleFix":"// before\ngcastRepository.sendMessage(ns, payload);\n// after\nif (!gcastRepository.isSessionEstablished) {\n  await gcastRepository.connectToDestination(deviceId);\n}\ngcastRepository.sendMessage(ns, payload);","handlingStrategy":"validation","validationCode":"if (gcastRepository.isSessionEstablished) {\n  gcastRepository.sendMessage(ns, payload);\n} else {\n  await reconnectAndSend(ns, payload);\n}","typeGuard":"bool canSendMessage(GcastRepository repo) => repo.isSessionEstablished;","tryCatchPattern":"try {\n  gcastRepository.sendMessage(ns, payload);\n} catch (e) {\n  if (e.toString().contains('Cast session is not established')) {\n    await gcastRepository.connectToDestination(deviceId);\n    gcastRepository.sendMessage(ns, payload);\n  } else {\n    rethrow;\n  }\n}","preventionTips":["Subscribe to cast session lifecycle callbacks and update UI state on suspend/end","Disable cast send buttons until a session is confirmed established","Re-establish the session on app resume before sending","Wrap send operations with a reconnect-once retry helper"],"tags":["cast","network","mobile"],"backgroundTag":"invalid-state-transition","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}