{"record":{"id":"9d369fb549c24818","repo":"NationalSecurityAgency/ghidra","slug":"cannot-rewind-a-negative-number-9d369f","errorCode":null,"errorMessage":"Cannot rewind a negative number","messagePattern":"Cannot rewind a negative number","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Sequence.java","lineNumber":188,"sourceCode":"\t\tfor (; toRemove > 0; toRemove--) {\n\t\t\tsteps.remove(steps.size() - 1);\n\t\t}\n\t}\n\n\t/**\n\t * Rewind this sequence the given step count\n\t * \n\t * <p>\n\t * This modifies the sequence in place, removing the given count from the end of the sequence.\n\t * Any step whose count is reduced to 0 as a result of rewinding is removed entirely from the\n\t * sequence. Note that each sleigh step (modification) counts as one step when rewinding.\n\t * \n\t * @param count the step count to rewind\n\t * @return if count exceeds the steps of this sequence, the (positive) difference remaining\n\t */\n\tpublic long rewind(long count) {\n\t\tif (count < 0) {\n\t\t\tthrow new IllegalArgumentException(\"Cannot rewind a negative number\");\n\t\t}\n\t\twhile (!steps.isEmpty()) {\n\t\t\tint lastIndex = steps.size() - 1;\n\t\t\tcount = steps.get(lastIndex).rewind(count);\n\t\t\tif (count >= 0) {\n\t\t\t\tsteps.remove(lastIndex);\n\t\t\t}\n\t\t\tif (count <= 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn Long.max(0, count);\n\t}\n\n\t/**\n\t * Drop the last step from this sequence\n\t * \n\t * @return the sequence with the last step removed","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Sequence.java#L170-L206","documentation":"Sequence.rewind throws when called with a negative count. Rewinding means removing the given number of steps from the end of the sequence; a negative count is logically meaningless. The method modifies the sequence in place, reducing each step's tick count from the tail until the requested count is consumed.","triggerScenarios":"Calling sequence.rewind(negativeNumber) directly, or calling it with a value computed from a subtraction that can go negative (e.g. rewind(this.getTotalTickCount() - other.getTotalTickCount()) when this has fewer ticks than other).","commonSituations":"Diffing two schedules and rewinding by the delta without clamping to zero. Using rewind in a loop where the count variable is decremented past zero by external logic.","solutions":["Clamp the count to zero before calling: rewind(Math.max(0, count))","Check the sign before calling: if (count >= 0) sequence.rewind(count)","If computing a delta between two sequences, ensure the result is non-negative or use Long.max(0, delta)"],"exampleFix":"// before\nsequence.rewind(delta); // delta may be negative\n// after\nif (delta > 0) {\n    sequence.rewind(delta);\n}","handlingStrategy":"validation","validationCode":"long safeCount = Math.max(0, requestedCount);\nif (safeCount > 0) {\n    sequence.rewind(safeCount);\n}","typeGuard":"// N/A — primitive long, guard with range check","tryCatchPattern":"try {\n    sequence.rewind(count);\n} catch (IllegalArgumentException e) {\n    // clamp count to 0 and retry, or report\n    sequence.rewind(0);\n}","preventionTips":["Always clamp computed rewind counts with Math.max(0, delta)","When diffing two schedules, verify the direction before rewinding","Treat negative rewind as a no-op rather than passing it through"],"tags":["ghidra","trace-schedule","sequence","precondition"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}