{"record":{"id":"c75b8a686f5237fd","repo":"airbnb/epoxy","slug":"timer-was-already-started","errorCode":null,"errorMessage":"Timer was already started","messagePattern":"Timer was already started","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/DebugTimer.java","lineNumber":24,"sourceCode":"\n  private final String tag;\n  private long startTime;\n  private String sectionName;\n\n  DebugTimer(String tag) {\n    this.tag = tag;\n    reset();\n  }\n\n  private void reset() {\n    startTime = -1;\n    sectionName = null;\n  }\n\n  @Override\n  public void start(String sectionName) {\n    if (startTime != -1) {\n      throw new IllegalStateException(\"Timer was already started\");\n    }\n\n    startTime = System.nanoTime();\n    this.sectionName = sectionName;\n  }\n\n  @Override\n  public void stop() {\n    if (startTime == -1) {\n      throw new IllegalStateException(\"Timer was not started\");\n    }\n\n    float durationMs = (System.nanoTime() - startTime) / 1000000f;\n    Log.d(tag, String.format(sectionName + \": %.3fms\", durationMs));\n    reset();\n  }\n}\n","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/DebugTimer.java#L6-L42","documentation":"DebugTimer.start() throws IllegalStateException if the timer's startTime is already set (not -1), meaning the same section was started twice without a stop()/reset() in between. The timer tracks a single start timestamp and section name, so a double start would silently overwrite measurements. The library fails fast to surface mis-sequenced timing calls.","triggerScenarios":"Calling start(sectionName) twice on the same DebugTimer instance without calling stop() or reset() between the calls.","commonSituations":"Debugging bind times in an Epoxy controller where the start call is inside a lifecycle method that runs more than once (e.g. onBindViewHolder re-binding the same holder, or a retry path), or accidentally creating two timing sections with the same shared timer instance.","solutions":["Call stop() (which auto-resets) after each start() before starting again","Call reset() before start() when the previous measurement is no longer needed","Create a new DebugTimer instance for each independent section","Guard the start call: only invoke start() if the section hasn't already been started"],"exampleFix":"// before\ntimer.start(\"bind\");\ntimer.start(\"bind\"); // throws\n\n// after\ntimer.reset();\ntimer.start(\"bind\");","handlingStrategy":"validation","validationCode":"// ensure the timer is not already running before starting\nif (timer.isStarted()) { timer.reset(); }\ntimer.start(\"bind\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair start() with stop() in try/finally","Use one timer instance per measured section","Call reset() defensively before start() in re-bind paths"],"tags":["debugging","timer","android","epoxy"],"backgroundTag":"invalid-state-transition","analyzedSha":"e45bd3a61fe3a1f130e184f5b8dcf172ab99025a","analyzedAt":"2026-09-13T03:24:16.052Z","contentChangedAt":"2026-09-13T03:24:16.052Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}