Files
adam-acs/references/tr069-state-machine-problems.md
T
Kevin L'HereecandContinue aad93911e4 feat: TR-069 CWMP state machine hardening + exchange viewer improvements
- TR-069 CWMP state machine hardening (14 phases): shared event utility,
  multi-event routing, GPN circuit breaker, IdleState GPN guard, expanded
  GPN fault codes, CriticalReadsState, ConfigEval GPN guard, task queue
  race condition fix, explicit error handling, terminal states
  (OperationalState/PartialOperationalState), consolidated reclassify,
  crash pattern detection, removed enqueueImmediate, state flow wiring
- Fix SOAP Fault not processed by state machine (soap-env:Fault detection)
- Fix GPN circuit breaker not persisting after session drops
- Fix CriticalReadsState GPV response parsing (xsi:type normalization)
- Fix CriticalReadsState not processing GPV response values
- Skip SPV when device is already compliant (ProvisioningCode check)
- Add Device.DeviceInfo.ModelName to critical reads GPV
- Fix TypeError on DeviceDetail parameters view (null guards)
- Log empty 204 responses in exchange trace UI
- TR-181 catalog: type corrections and new entries (+6640 lines)
- SequenceViewer: replace session-nav buttons with compact dropdown select
- SequenceViewer: fix CPE/ACS lifeline headers wrapping to 2 lines
- Add 20 unit tests for state machine fixes (SOAP Fault, GPN circuit
  breaker, CriticalReads, stale session, ConfigEval/ComplianceUpdate skips)

Generated with [Continue](https://continue.dev)

Co-Authored-By: Continue <noreply@continue.dev>
2026-06-08 12:49:00 +00:00

4.2 KiB

TR-069 CWMP State Machine — Problem Catalog

CRITIQUE-1: Race Condition task queue / state machine

  • File: statemachinemanager.ts (continue() + setupTaskQueueListeners())
  • Detail: addTask() in DeviceActionExecutor emits 'taskSent' synchronously, setting lastTaskId BEFORE the XML is returned to the CPE. When both the state machine and task queue serve tasks in the same session, lastTaskId can point to the wrong task, corrupting CPE response routing.
  • Impact: Responses routed to wrong callback, silent data corruption, session hangs.

CRITIQUE-2: Multiple superfluous sessions on BOOTSTRAP

  • File: cwmpcontroller.ts (handleInform()) + statemachinemanager.ts (startStateMachine())
  • Detail: enqueueImmediate(GPV) in the controller + implicit ScheduleInform via SPV systematically generate 3 sessions instead of 1.
  • Impact: 3x ACS load per bootstrap, unnecessary CPE wake-ups, slower onboarding.

CRITIQUE-3: IdleState does not check gpnStatus before sending GPN

  • File: mainstatemachine.ts (IdleState.handle())
  • Detail: A device with gpnStatus='unsupported' (Livebox, buggy CPEs) still receives a GPN every session, causing systematic Fault or CPE crash/reboot.
  • Impact: CPE crash loops, Fault flooding, device never reaches operational state.

CRITIQUE-4: Silent safetyGuard in executeCurrentState()

  • File: mainstatemachine.ts (executeCurrentState())
  • Detail: The while (safetyGuard++ < 15) loop stops silently if the machine loops. No log of blocking state, no transition to an explicit error state.
  • Impact: Sessions terminate with empty response, device not marked in error, monitoring blind spot.

MAJEUR-5: Multi-event routing based on LAST event only

  • File: statemachinemanager.ts (startStateMachine())
  • Code: const lastEvent = this.normalizeEvent(evts[evts.length - 1])
  • Detail: An Inform with ['0 BOOTSTRAP', '1 BOOT'] processes only '1 BOOT'. An Inform with ['6 CONNECTION REQUEST', '2 PERIODIC'] forces ImmediateCommandStateMachine when onboarding is required.
  • Impact: Wrong state machine selected, missed onboarding, incorrect provisioning flow.

MAJEUR-6: Incomplete GPN fault codes in DeviceActionExecutor

  • File: deviceactionexecutor.ts (getParameterNames() wrappedCallback)
  • Code: if (code === 9000 || code === 9001) { device.setGpnStatus('unsupported') }
  • Detail: Codes 9003 (Invalid Arguments), 9004 (Resources Exceeded), 9005 (Invalid Parameter Name) should also trigger fallback. Network/timeout codes should NOT mark unsupported.
  • Impact: Missing fallback on legitimate GPN failures, false unsupported marking on transient errors.

MAJEUR-7: No GPN circuit breaker across sessions

  • Files: mainstatemachine.ts, configurationevaluationstate.ts, deviceactionexecutor.ts
  • Detail: No failure counter persisted between sessions. A CPE that crashes on GPN will be retried every Inform. No cooldown, no crash pattern detection.
  • Impact: Infinite crash loops for vulnerable CPEs, ACS resource waste.

MAJEUR-8: Critical GPV outside state machine (collision with ConfigEvalState)

  • File: cwmpcontroller.ts (handleInform() enqueueImmediate block)
  • Detail: GPV (HardwareVersion, SoftwareVersion, ConnectionRequestURL) is served by the task queue, not the state machine. It can interleave with GPN/GPV from ConfigurationEvaluationState, corrupting CWMP exchange order.
  • Impact: Out-of-order CWMP messages, session corruption, device misidentification.

MINEUR-9: Duplicated EventCode normalization

  • Files: cwmpcontroller.ts (normalizeEventCode()), statemachinemanager.ts (normalizeEvent())
  • Detail: Two identical functions, two maintenance points.
  • Impact: Drift risk, inconsistent normalization.

MINEUR-10: reclassifyDeviceFromCatalog() called at 4 different places

  • Files: cwmpcontroller.ts, deviceactionexecutor.ts (x2), mainstatemachine.ts (CompletedState)
  • Detail: Scattered fallback logic, hard to unit test.
  • Impact: Inconsistent reclassification timing, test complexity.

MINEUR-11: No explicit PartialOperational state

  • File: configurationevaluationstate.ts (catch block -> { isComplete: true })
  • Detail: On ConfigEval failure, the machine terminates without persisting a degraded status usable for monitoring/alerting.
  • Impact: Monitoring blind to degraded devices, no alerting on partial provisioning.