rimbisect 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .pytest_cache/
4
+ .venv/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ runs/
9
+ probe-src/bin/
10
+ probe-src/obj/
11
+ .tools/
12
+ .phase0/
13
+ acceptance/work/
14
+ acceptance/mapfail-src/bin/
15
+ acceptance/mapfail-src/obj/
16
+ acceptance/flood-src/bin/
17
+ acceptance/flood-src/obj/
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 - 2026-09-30
4
+
5
+ First release.
6
+
7
+ - `rimbisect bisect` finds the mod, or the set of mods that only fail together, behind
8
+ an error you pick from the log, a piece of text, a regex, a slow start or a crash.
9
+ - Trials keep dependencies loaded and your load order intact, and run on a throwaway
10
+ copy of your Config folder, so your mod list, settings and saves are never written.
11
+ - A confirm run without the culprit finds separate causes of the same error.
12
+ - `rimbisect check` saves a known good list; the next bisect tries recently changed
13
+ mods first.
14
+ - `rimbisect mods` shows the mod list with missing dependencies and incompatibilities.
15
+
16
+ See the README for how it works and what it can't do.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christiaan Bosch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,317 @@
1
+ Metadata-Version: 2.5
2
+ Name: rimbisect
3
+ Version: 0.1.0
4
+ Summary: Find the RimWorld mod, or combination of mods, behind an error by relaunching the game on halves of your load order.
5
+ Project-URL: Homepage, https://github.com/Booyaka101/rimbisect
6
+ Project-URL: Repository, https://github.com/Booyaka101/rimbisect
7
+ Project-URL: Issues, https://github.com/Booyaka101/rimbisect/issues
8
+ Project-URL: Changelog, https://github.com/Booyaka101/rimbisect/blob/main/CHANGELOG.md
9
+ Author-email: Christiaan Bosch <cbosch101@gmail.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: bisect,debugging,load-order,modding,mods,rimworld,troubleshooting
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: Microsoft :: Windows
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Games/Entertainment
22
+ Classifier: Topic :: Software Development :: Debuggers
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: lxml>=5.2.2
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=8.0; extra == 'dev'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # rimbisect
30
+
31
+ Find the RimWorld mod, or combination of mods, behind an error, unattended.
32
+
33
+ You have 300 mods and a red error at startup, or a map that never finishes loading. The
34
+ usual fix is to disable half your list, start the game, see if the error is still there,
35
+ and repeat until you are down to one mod. That takes an evening and it is easy to get
36
+ wrong once dependencies are involved. rimbisect does the same halving for you. It
37
+ launches the game on dependency-safe parts of your mod list, reads each run's log,
38
+ closes the game and narrows down, until it can name the mod.
39
+
40
+ It never writes to your real mod list, settings or saves, and never reorders your mods.
41
+ The only thing it puts in the game folder is a small helper mod, `Mods\rimbisect-probe`,
42
+ which it deletes when the run ends.
43
+
44
+ ## Install
45
+
46
+ You need Windows, RimWorld 1.6 from Steam, and Python 3.11 or newer. If you don't have
47
+ Python, get it from [python.org](https://www.python.org/downloads/) and tick "Add
48
+ python.exe to PATH" in the installer. Then, in PowerShell:
49
+
50
+ ```
51
+ py -m pip install rimbisect
52
+ ```
53
+
54
+ If `rimbisect` is then "not recognized", Python's Scripts folder isn't on your PATH;
55
+ `py -m rimbisect` works the same everywhere. With pipx, `pipx install rimbisect` does it
56
+ in one go.
57
+
58
+ ## Use it
59
+
60
+ Close RimWorld, make sure Steam is running (the game only loads Workshop mods through
61
+ it), and start a bisect:
62
+
63
+ ```
64
+ rimbisect bisect
65
+ ```
66
+
67
+ rimbisect finds your install through Steam and takes the mod list you have active in the
68
+ game. It first runs your full list once and shows the errors it logged, grouped and
69
+ numbered. Type the number of the one you want gone, then leave it. (`--pick N` answers
70
+ ahead of time, but the numbers can shift between runs when errors come and go.) The game
71
+ opens and closes on its own for every trial; don't click in it, and don't use the PC for
72
+ games meanwhile. Expect 10 to 20 trials, each one a game start. A 200 mod list took 12
73
+ minutes on a fast PC; count on two to three times as long as your full list takes to load
74
+ into a map. Ctrl+C stops the run at any point and closes the game.
75
+
76
+ If you already know what the error says, skip the question. `--match-text` takes a piece
77
+ of the error as you see it in the log (`Player.log`, next to the `Config` folder
78
+ mentioned below); `--match` takes a regular expression:
79
+
80
+ ```
81
+ rimbisect bisect --match-text "doesn't correspond to any field"
82
+ ```
83
+
84
+ This is a real run on a 200 mod list with a test mod that has a broken def at position
85
+ 150. It was made for testing with `--config`, which is why the paths are in a repo; yours
86
+ go to `%LOCALAPPDATA%\rimbisect\runs`.
87
+
88
+ ```
89
+ rimbisect bisect --config acceptance\work\ModsConfig-200.xml --workdir acceptance\work\rb --match rimbisectNoSuchField
90
+ ```
91
+
92
+ Progress goes to the terminal as it happens:
93
+
94
+ ```
95
+ rimbisect 0.1.0: 200 active mods, 194 to search
96
+ run folder D:\Repos\ideas\rimbisect\acceptance\work\rb\runs\2026-09-30_12-34-49
97
+ trial 1 baseline 200 mods FAIL 78.0s
98
+ the full list took 78s; expect about 11 more trials for a single culprit, 20 for two mods that only fail together
99
+ trial 2 bisect 103 mods PASS 114.4s
100
+ trial 3 bisect 57 mods FAIL 8.3s
101
+ trial 4 bisect 31 mods PASS 41.5s
102
+ trial 5 bisect 23 mods PASS 54.8s
103
+ trial 6 bisect 20 mods FAIL 5.1s
104
+ trial 7 bisect 13 mods PASS 34.7s
105
+ trial 8 bisect 11 mods PASS 35.7s
106
+ trial 9 bisect 11 mods FAIL 4.6s
107
+ trial 10 bisect 9 mods PASS 39.5s
108
+ trial 11 bisect 7 mods FAIL 2.8s
109
+ trial 12 without the culprits 199 mods PASS 320.1s
110
+ ```
111
+
112
+ and the report at the end:
113
+
114
+ ```
115
+ rimbisect 0.1.0 found the mods that cause the error
116
+
117
+ game D:\SteamLibrary\steamapps\common\RimWorld (1.6.4871 rev590)
118
+ mod list D:\Repos\ideas\rimbisect\acceptance\work\ModsConfig-200.xml (200 active, 194 searched)
119
+ error log matches /rimbisectNoSuchField/
120
+ run folder D:\Repos\ideas\rimbisect\acceptance\work\rb\runs\2026-09-30_12-34-49
121
+
122
+ CULPRIT rimbisect acceptance: broken def (rimbisect.acceptance.brokendef)
123
+ workshop -
124
+ folder D:\SteamLibrary\steamapps\common\RimWorld\Mods\rimbisect-acceptance-brokendef
125
+ modified 2026-09-30T09:23:26
126
+
127
+ Your mod list without 1 mod:
128
+ D:\Repos\ideas\rimbisect\acceptance\work\rb\runs\2026-09-30_12-34-49\ModsConfig.fixed.xml
129
+ note: the probe closed windows that paused the game in trials 2, 12: Dialog_ModFeatures, Dialog_UpdateFeatures
130
+
131
+ TRIALS 12 runs, 12m20s total
132
+
133
+ # label mods outcome time
134
+ 1 baseline 200 FAIL 78.0s
135
+ | XML error: <rimbisectNoSuchField>1</rimbisectNoSuchField> doesn't correspond to any field in type ThingCategoryDef. Context: <ThingCategoryDef><def...
136
+ 2 bisect 103 PASS 114.4s
137
+ ...
138
+ ```
139
+
140
+ Trial 12 is the check at the end: the full list without the culprit, which no longer
141
+ shows the error. The two windows in the note are HugsLib's update news and the feature
142
+ popup of Achtung and Camera+, which would otherwise have kept the game paused.
143
+
144
+ Six of the 200 mods (Core, the five DLCs) are always loaded, so 194 were searched. The run
145
+ folder keeps `report.txt`, `report.json`, the fixed mod list and every trial's log. Old
146
+ run folders are safe to delete.
147
+
148
+ The simplest fix is to disable the culprit in the game's mod manager. The fixed list is
149
+ your list without the culprits and without the mods that need them; to use it, close
150
+ RimWorld, back up `ModsConfig.xml` in
151
+ `%USERPROFILE%\AppData\LocalLow\Ludeon Studios\RimWorld by Ludeon Studios\Config`, and copy
152
+ `ModsConfig.fixed.xml` over it. To undo, put the backup back.
153
+
154
+ ## What counts as the error
155
+
156
+ A trial fails when:
157
+
158
+ - the error you picked from the full list's log shows up again (the default). Numbers,
159
+ hex ids and quoted names are masked before comparing, and for an exception only its
160
+ type and the method it came from count, so the same error from a different pawn still
161
+ counts.
162
+ - a log line or error contains `--match-text TEXT`, or matches `--match REGEX`.
163
+ - the map takes longer than `--slower-than SECONDS` to be ready, for slow starts.
164
+ - the game crashes, exits early, gives up on the map or drops back to Core alone after a
165
+ load error, with `--crash-is-fail`. Without
166
+ it those trials are reported as CRASH, run again once (big lists do crash now and then
167
+ for no repeatable reason), and treated as passing if the game crashes again. With it
168
+ and without `--match`, `--match-text`, `--slower-than` or `--pick`, the crash itself is
169
+ what rimbisect hunts.
170
+
171
+ Only one of `--match`, `--match-text` and `--slower-than` can be given.
172
+
173
+ A trial passes when the map has run for `--settle` seconds of game time (20 by default)
174
+ without the error. Raise it if your error only shows up a while into the game. A PC that
175
+ runs the game too slowly to get there still stops after three times as long in real time
176
+ (at least a minute), and the report points out trials that got through less than half of
177
+ it. If the full list does not reproduce the error, rimbisect says so and stops.
178
+
179
+ A trial that gives no answer, because it hit `--timeout` or the game did not load all the
180
+ mods it was given, is run again once. If the same list gives no answer twice, rimbisect
181
+ stops with the status "inconclusive" and says which list it was. It also stops that way
182
+ when the mods it narrowed down to don't show the error when run alone, which is what an
183
+ error that only shows up some of the time looks like; `--repeats 3` helps there.
184
+
185
+ ## How long it takes
186
+
187
+ Every trial is a full game start, so time depends on your PC and the size of the list.
188
+ On the run above (an i9-14900K, game on an NVMe SSD, RimWorld 1.6.4871) the full 199 mod
189
+ list took 5 minutes to reach a map and run it for 20 game seconds, a list of about 100
190
+ mods took under two minutes, and trials under 30 mods took 35 to 55 seconds. A failing
191
+ trial is often much quicker: the run stops the moment the error appears, and def errors
192
+ appear before the map is generated.
193
+
194
+ Trials needed after the first full run, measured with
195
+ [`acceptance/simulate.py`](https://github.com/Booyaka101/rimbisect/blob/main/acceptance/simulate.py) over every
196
+ single culprit and 300 random pairs:
197
+
198
+ | mods | one culprit | two mods that only fail together |
199
+ | ---: | --- | --- |
200
+ | 64 | 8.6 on average, at most 11 | 16.2 on average, at most 21 |
201
+ | 200 | 10.6 on average, at most 13 | 19.9 on average, at most 26 |
202
+ | 400 | 11.7 on average, at most 15 | 22.4 on average, at most 28 |
203
+
204
+ The real 200 mod run above needed 11, as the estimate after the full run said. For 400
205
+ mods, count on about 12 trials plus the full run. The first trials load half the list and
206
+ the last one nearly all of it, so they take most of the time; a single culprit in 400
207
+ mods should take somewhere around 25 to 40 minutes on a PC like this one. That is an
208
+ estimate from the 200 mod timings, not a measurement.
209
+
210
+ ## How it works
211
+
212
+ Each trial runs the game with `-savedatafolder` pointing at the run folder, which holds a
213
+ copy of your `Config` folder (mod settings included), HugsLib's settings and the trial's
214
+ mod list. The copy is made again before every trial, so a setting one trial changes
215
+ doesn't leak into the next, and it is deleted when the run ends. The copied `Prefs.xml`
216
+ runs the game in a 1280x720 window with the sound off and the UI scale at 1. Your own
217
+ `ModsConfig.xml` and `Prefs.xml` are only read, and your saves are never opened.
218
+
219
+ For the run, rimbisect copies a small mod, `rimbisect-probe`, into the game's `Mods`
220
+ folder and deletes it afterwards, also on Ctrl+C. It loads last, writes errors and
221
+ progress to a file rimbisect watches, and closes the game once the map has settled. It
222
+ has no Harmony patches and does nothing when rimbisect did not start the game. If
223
+ rimbisect is killed hard, delete `Mods\rimbisect-probe` yourself or let the next run
224
+ replace it.
225
+
226
+ RimWorld stops logging for good after 10,000 messages, which a big list can reach while
227
+ loading. The probe keeps the game's count from getting there, and switches logging back
228
+ on if it was reached before the probe loaded, so a later error is still seen.
229
+
230
+ The probe also reports which mods the game actually loaded. If a trial is missing some
231
+ (usually because Steam was closed mid-run), it counts as no answer rather than a pass.
232
+ Windows that pause a new game, like HugsLib's update news or the feature popups of
233
+ Achtung and Camera+, are closed so the map actually runs; the report says which ones.
234
+
235
+ Every trial includes the mods that the mods under test need, so there are no "missing
236
+ dependency" errors that the full list doesn't have. Missing dependencies are never
237
+ invented; rimbisect warns about them instead, and about pairs of active mods that say
238
+ they are incompatible. A trial is your list with some mods left out, never reordered.
239
+ Mods given with `--keep` are loaded in every trial, together with what they need.
240
+
241
+ When neither half fails on its own, rimbisect narrows each half with the other one
242
+ loaded, so two or three mods that only break together come out as a set.
243
+
244
+ Once it has a culprit, rimbisect runs the list once more without it and the mods that
245
+ need it. If the error is still there, something else causes it too, and the search goes
246
+ on through the rest. The report lists each cause on its own.
247
+
248
+ `rimbisect check` runs your full list once and, if it gets through, remembers it as the
249
+ last good state. The next bisect first tries the list without the mods whose folders
250
+ changed since then (updates, new mods). `--since DATE` does the same without a check.
251
+
252
+ For errors that come and go, `--repeats N` runs a passing list again, up to N times,
253
+ before trusting it. Lists that gave different results are listed in the report.
254
+
255
+ rimbisect only kills the game process it started, by process id. The game runs in a
256
+ Windows job object, so it also closes if rimbisect is killed or its window is closed.
257
+
258
+ ## Commands
259
+
260
+ | | |
261
+ | --- | --- |
262
+ | `rimbisect bisect` | Find the mods behind an error |
263
+ | `rimbisect check` | Run the full list once; if it gets through, save it as the last good state |
264
+ | `rimbisect mods` | Show the mod list the way rimbisect reads it, with warnings |
265
+
266
+ Every command takes these:
267
+
268
+ | Option | |
269
+ | --- | --- |
270
+ | `--game PATH` | RimWorld install folder. Found through Steam if omitted |
271
+ | `--config FILE` | `ModsConfig.xml` to test instead of the game's own. Only read |
272
+ | `--json` | Print JSON instead of text |
273
+
274
+ `bisect` and `check` also take these:
275
+
276
+ | Option | |
277
+ | --- | --- |
278
+ | `--workdir PATH` | Where runs and `last-good.json` go. Default `%LOCALAPPDATA%\rimbisect` |
279
+ | `--match REGEX` | Fail when an error or log line matches |
280
+ | `--match-text TEXT` | Fail when an error or log line contains TEXT, taken literally |
281
+ | `--slower-than SECONDS` | Fail when the map is not ready after this long |
282
+ | `--settle SECONDS` | Game seconds a map has to run without the error to pass. Default 20 |
283
+ | `--timeout MINUTES` | Give up on a single trial after this long. Default 20 |
284
+
285
+ and `bisect` these:
286
+
287
+ | Option | |
288
+ | --- | --- |
289
+ | `--pick N` | Hunt error N from the full list's errors without asking |
290
+ | `--keep ID[,ID]` | Load these packageIds in every trial. Core and DLCs always are |
291
+ | `--since DATE` | First try the list without mods changed after DATE, like `2026-09-01` |
292
+ | `--ignore-last-good` | Don't try the mods changed since the last good run first |
293
+ | `--repeats N` | Run a passing list up to N times, for errors that come and go |
294
+ | `--crash-is-fail` | Count a crash, early exit or failed map as the problem |
295
+
296
+ `rimbisect --version` prints the version.
297
+
298
+ Exit codes: 0 when culprits were found (or `check` passed), 1 when the full list did not
299
+ reproduce the error, the base game alone fails, the search was inconclusive, or `check`
300
+ failed, 2 for usage errors such as a missing install or RimWorld already running, 130
301
+ when interrupted.
302
+
303
+ ## Limitations
304
+
305
+ - Windows only, and only the Steam version has been tried. The probe is built for 1.6.
306
+ - It finds errors that happen while the game starts and during the first `--settle`
307
+ seconds on a fresh map. Problems that need your save, a raid or a year of play are out
308
+ of reach.
309
+ - Settings of mods that keep them outside `Config` (other than HugsLib) are not copied,
310
+ so those mods run with their defaults.
311
+ - A problem that depends on load order, rather than on which mods are present, is only
312
+ found as the set of mods involved. rimbisect does not try other orders.
313
+ - RimWorld has to be closed while it runs, and the machine is busy the whole time.
314
+
315
+ ## License
316
+
317
+ MIT
@@ -0,0 +1,289 @@
1
+ # rimbisect
2
+
3
+ Find the RimWorld mod, or combination of mods, behind an error, unattended.
4
+
5
+ You have 300 mods and a red error at startup, or a map that never finishes loading. The
6
+ usual fix is to disable half your list, start the game, see if the error is still there,
7
+ and repeat until you are down to one mod. That takes an evening and it is easy to get
8
+ wrong once dependencies are involved. rimbisect does the same halving for you. It
9
+ launches the game on dependency-safe parts of your mod list, reads each run's log,
10
+ closes the game and narrows down, until it can name the mod.
11
+
12
+ It never writes to your real mod list, settings or saves, and never reorders your mods.
13
+ The only thing it puts in the game folder is a small helper mod, `Mods\rimbisect-probe`,
14
+ which it deletes when the run ends.
15
+
16
+ ## Install
17
+
18
+ You need Windows, RimWorld 1.6 from Steam, and Python 3.11 or newer. If you don't have
19
+ Python, get it from [python.org](https://www.python.org/downloads/) and tick "Add
20
+ python.exe to PATH" in the installer. Then, in PowerShell:
21
+
22
+ ```
23
+ py -m pip install rimbisect
24
+ ```
25
+
26
+ If `rimbisect` is then "not recognized", Python's Scripts folder isn't on your PATH;
27
+ `py -m rimbisect` works the same everywhere. With pipx, `pipx install rimbisect` does it
28
+ in one go.
29
+
30
+ ## Use it
31
+
32
+ Close RimWorld, make sure Steam is running (the game only loads Workshop mods through
33
+ it), and start a bisect:
34
+
35
+ ```
36
+ rimbisect bisect
37
+ ```
38
+
39
+ rimbisect finds your install through Steam and takes the mod list you have active in the
40
+ game. It first runs your full list once and shows the errors it logged, grouped and
41
+ numbered. Type the number of the one you want gone, then leave it. (`--pick N` answers
42
+ ahead of time, but the numbers can shift between runs when errors come and go.) The game
43
+ opens and closes on its own for every trial; don't click in it, and don't use the PC for
44
+ games meanwhile. Expect 10 to 20 trials, each one a game start. A 200 mod list took 12
45
+ minutes on a fast PC; count on two to three times as long as your full list takes to load
46
+ into a map. Ctrl+C stops the run at any point and closes the game.
47
+
48
+ If you already know what the error says, skip the question. `--match-text` takes a piece
49
+ of the error as you see it in the log (`Player.log`, next to the `Config` folder
50
+ mentioned below); `--match` takes a regular expression:
51
+
52
+ ```
53
+ rimbisect bisect --match-text "doesn't correspond to any field"
54
+ ```
55
+
56
+ This is a real run on a 200 mod list with a test mod that has a broken def at position
57
+ 150. It was made for testing with `--config`, which is why the paths are in a repo; yours
58
+ go to `%LOCALAPPDATA%\rimbisect\runs`.
59
+
60
+ ```
61
+ rimbisect bisect --config acceptance\work\ModsConfig-200.xml --workdir acceptance\work\rb --match rimbisectNoSuchField
62
+ ```
63
+
64
+ Progress goes to the terminal as it happens:
65
+
66
+ ```
67
+ rimbisect 0.1.0: 200 active mods, 194 to search
68
+ run folder D:\Repos\ideas\rimbisect\acceptance\work\rb\runs\2026-09-30_12-34-49
69
+ trial 1 baseline 200 mods FAIL 78.0s
70
+ the full list took 78s; expect about 11 more trials for a single culprit, 20 for two mods that only fail together
71
+ trial 2 bisect 103 mods PASS 114.4s
72
+ trial 3 bisect 57 mods FAIL 8.3s
73
+ trial 4 bisect 31 mods PASS 41.5s
74
+ trial 5 bisect 23 mods PASS 54.8s
75
+ trial 6 bisect 20 mods FAIL 5.1s
76
+ trial 7 bisect 13 mods PASS 34.7s
77
+ trial 8 bisect 11 mods PASS 35.7s
78
+ trial 9 bisect 11 mods FAIL 4.6s
79
+ trial 10 bisect 9 mods PASS 39.5s
80
+ trial 11 bisect 7 mods FAIL 2.8s
81
+ trial 12 without the culprits 199 mods PASS 320.1s
82
+ ```
83
+
84
+ and the report at the end:
85
+
86
+ ```
87
+ rimbisect 0.1.0 found the mods that cause the error
88
+
89
+ game D:\SteamLibrary\steamapps\common\RimWorld (1.6.4871 rev590)
90
+ mod list D:\Repos\ideas\rimbisect\acceptance\work\ModsConfig-200.xml (200 active, 194 searched)
91
+ error log matches /rimbisectNoSuchField/
92
+ run folder D:\Repos\ideas\rimbisect\acceptance\work\rb\runs\2026-09-30_12-34-49
93
+
94
+ CULPRIT rimbisect acceptance: broken def (rimbisect.acceptance.brokendef)
95
+ workshop -
96
+ folder D:\SteamLibrary\steamapps\common\RimWorld\Mods\rimbisect-acceptance-brokendef
97
+ modified 2026-09-30T09:23:26
98
+
99
+ Your mod list without 1 mod:
100
+ D:\Repos\ideas\rimbisect\acceptance\work\rb\runs\2026-09-30_12-34-49\ModsConfig.fixed.xml
101
+ note: the probe closed windows that paused the game in trials 2, 12: Dialog_ModFeatures, Dialog_UpdateFeatures
102
+
103
+ TRIALS 12 runs, 12m20s total
104
+
105
+ # label mods outcome time
106
+ 1 baseline 200 FAIL 78.0s
107
+ | XML error: <rimbisectNoSuchField>1</rimbisectNoSuchField> doesn't correspond to any field in type ThingCategoryDef. Context: <ThingCategoryDef><def...
108
+ 2 bisect 103 PASS 114.4s
109
+ ...
110
+ ```
111
+
112
+ Trial 12 is the check at the end: the full list without the culprit, which no longer
113
+ shows the error. The two windows in the note are HugsLib's update news and the feature
114
+ popup of Achtung and Camera+, which would otherwise have kept the game paused.
115
+
116
+ Six of the 200 mods (Core, the five DLCs) are always loaded, so 194 were searched. The run
117
+ folder keeps `report.txt`, `report.json`, the fixed mod list and every trial's log. Old
118
+ run folders are safe to delete.
119
+
120
+ The simplest fix is to disable the culprit in the game's mod manager. The fixed list is
121
+ your list without the culprits and without the mods that need them; to use it, close
122
+ RimWorld, back up `ModsConfig.xml` in
123
+ `%USERPROFILE%\AppData\LocalLow\Ludeon Studios\RimWorld by Ludeon Studios\Config`, and copy
124
+ `ModsConfig.fixed.xml` over it. To undo, put the backup back.
125
+
126
+ ## What counts as the error
127
+
128
+ A trial fails when:
129
+
130
+ - the error you picked from the full list's log shows up again (the default). Numbers,
131
+ hex ids and quoted names are masked before comparing, and for an exception only its
132
+ type and the method it came from count, so the same error from a different pawn still
133
+ counts.
134
+ - a log line or error contains `--match-text TEXT`, or matches `--match REGEX`.
135
+ - the map takes longer than `--slower-than SECONDS` to be ready, for slow starts.
136
+ - the game crashes, exits early, gives up on the map or drops back to Core alone after a
137
+ load error, with `--crash-is-fail`. Without
138
+ it those trials are reported as CRASH, run again once (big lists do crash now and then
139
+ for no repeatable reason), and treated as passing if the game crashes again. With it
140
+ and without `--match`, `--match-text`, `--slower-than` or `--pick`, the crash itself is
141
+ what rimbisect hunts.
142
+
143
+ Only one of `--match`, `--match-text` and `--slower-than` can be given.
144
+
145
+ A trial passes when the map has run for `--settle` seconds of game time (20 by default)
146
+ without the error. Raise it if your error only shows up a while into the game. A PC that
147
+ runs the game too slowly to get there still stops after three times as long in real time
148
+ (at least a minute), and the report points out trials that got through less than half of
149
+ it. If the full list does not reproduce the error, rimbisect says so and stops.
150
+
151
+ A trial that gives no answer, because it hit `--timeout` or the game did not load all the
152
+ mods it was given, is run again once. If the same list gives no answer twice, rimbisect
153
+ stops with the status "inconclusive" and says which list it was. It also stops that way
154
+ when the mods it narrowed down to don't show the error when run alone, which is what an
155
+ error that only shows up some of the time looks like; `--repeats 3` helps there.
156
+
157
+ ## How long it takes
158
+
159
+ Every trial is a full game start, so time depends on your PC and the size of the list.
160
+ On the run above (an i9-14900K, game on an NVMe SSD, RimWorld 1.6.4871) the full 199 mod
161
+ list took 5 minutes to reach a map and run it for 20 game seconds, a list of about 100
162
+ mods took under two minutes, and trials under 30 mods took 35 to 55 seconds. A failing
163
+ trial is often much quicker: the run stops the moment the error appears, and def errors
164
+ appear before the map is generated.
165
+
166
+ Trials needed after the first full run, measured with
167
+ [`acceptance/simulate.py`](https://github.com/Booyaka101/rimbisect/blob/main/acceptance/simulate.py) over every
168
+ single culprit and 300 random pairs:
169
+
170
+ | mods | one culprit | two mods that only fail together |
171
+ | ---: | --- | --- |
172
+ | 64 | 8.6 on average, at most 11 | 16.2 on average, at most 21 |
173
+ | 200 | 10.6 on average, at most 13 | 19.9 on average, at most 26 |
174
+ | 400 | 11.7 on average, at most 15 | 22.4 on average, at most 28 |
175
+
176
+ The real 200 mod run above needed 11, as the estimate after the full run said. For 400
177
+ mods, count on about 12 trials plus the full run. The first trials load half the list and
178
+ the last one nearly all of it, so they take most of the time; a single culprit in 400
179
+ mods should take somewhere around 25 to 40 minutes on a PC like this one. That is an
180
+ estimate from the 200 mod timings, not a measurement.
181
+
182
+ ## How it works
183
+
184
+ Each trial runs the game with `-savedatafolder` pointing at the run folder, which holds a
185
+ copy of your `Config` folder (mod settings included), HugsLib's settings and the trial's
186
+ mod list. The copy is made again before every trial, so a setting one trial changes
187
+ doesn't leak into the next, and it is deleted when the run ends. The copied `Prefs.xml`
188
+ runs the game in a 1280x720 window with the sound off and the UI scale at 1. Your own
189
+ `ModsConfig.xml` and `Prefs.xml` are only read, and your saves are never opened.
190
+
191
+ For the run, rimbisect copies a small mod, `rimbisect-probe`, into the game's `Mods`
192
+ folder and deletes it afterwards, also on Ctrl+C. It loads last, writes errors and
193
+ progress to a file rimbisect watches, and closes the game once the map has settled. It
194
+ has no Harmony patches and does nothing when rimbisect did not start the game. If
195
+ rimbisect is killed hard, delete `Mods\rimbisect-probe` yourself or let the next run
196
+ replace it.
197
+
198
+ RimWorld stops logging for good after 10,000 messages, which a big list can reach while
199
+ loading. The probe keeps the game's count from getting there, and switches logging back
200
+ on if it was reached before the probe loaded, so a later error is still seen.
201
+
202
+ The probe also reports which mods the game actually loaded. If a trial is missing some
203
+ (usually because Steam was closed mid-run), it counts as no answer rather than a pass.
204
+ Windows that pause a new game, like HugsLib's update news or the feature popups of
205
+ Achtung and Camera+, are closed so the map actually runs; the report says which ones.
206
+
207
+ Every trial includes the mods that the mods under test need, so there are no "missing
208
+ dependency" errors that the full list doesn't have. Missing dependencies are never
209
+ invented; rimbisect warns about them instead, and about pairs of active mods that say
210
+ they are incompatible. A trial is your list with some mods left out, never reordered.
211
+ Mods given with `--keep` are loaded in every trial, together with what they need.
212
+
213
+ When neither half fails on its own, rimbisect narrows each half with the other one
214
+ loaded, so two or three mods that only break together come out as a set.
215
+
216
+ Once it has a culprit, rimbisect runs the list once more without it and the mods that
217
+ need it. If the error is still there, something else causes it too, and the search goes
218
+ on through the rest. The report lists each cause on its own.
219
+
220
+ `rimbisect check` runs your full list once and, if it gets through, remembers it as the
221
+ last good state. The next bisect first tries the list without the mods whose folders
222
+ changed since then (updates, new mods). `--since DATE` does the same without a check.
223
+
224
+ For errors that come and go, `--repeats N` runs a passing list again, up to N times,
225
+ before trusting it. Lists that gave different results are listed in the report.
226
+
227
+ rimbisect only kills the game process it started, by process id. The game runs in a
228
+ Windows job object, so it also closes if rimbisect is killed or its window is closed.
229
+
230
+ ## Commands
231
+
232
+ | | |
233
+ | --- | --- |
234
+ | `rimbisect bisect` | Find the mods behind an error |
235
+ | `rimbisect check` | Run the full list once; if it gets through, save it as the last good state |
236
+ | `rimbisect mods` | Show the mod list the way rimbisect reads it, with warnings |
237
+
238
+ Every command takes these:
239
+
240
+ | Option | |
241
+ | --- | --- |
242
+ | `--game PATH` | RimWorld install folder. Found through Steam if omitted |
243
+ | `--config FILE` | `ModsConfig.xml` to test instead of the game's own. Only read |
244
+ | `--json` | Print JSON instead of text |
245
+
246
+ `bisect` and `check` also take these:
247
+
248
+ | Option | |
249
+ | --- | --- |
250
+ | `--workdir PATH` | Where runs and `last-good.json` go. Default `%LOCALAPPDATA%\rimbisect` |
251
+ | `--match REGEX` | Fail when an error or log line matches |
252
+ | `--match-text TEXT` | Fail when an error or log line contains TEXT, taken literally |
253
+ | `--slower-than SECONDS` | Fail when the map is not ready after this long |
254
+ | `--settle SECONDS` | Game seconds a map has to run without the error to pass. Default 20 |
255
+ | `--timeout MINUTES` | Give up on a single trial after this long. Default 20 |
256
+
257
+ and `bisect` these:
258
+
259
+ | Option | |
260
+ | --- | --- |
261
+ | `--pick N` | Hunt error N from the full list's errors without asking |
262
+ | `--keep ID[,ID]` | Load these packageIds in every trial. Core and DLCs always are |
263
+ | `--since DATE` | First try the list without mods changed after DATE, like `2026-09-01` |
264
+ | `--ignore-last-good` | Don't try the mods changed since the last good run first |
265
+ | `--repeats N` | Run a passing list up to N times, for errors that come and go |
266
+ | `--crash-is-fail` | Count a crash, early exit or failed map as the problem |
267
+
268
+ `rimbisect --version` prints the version.
269
+
270
+ Exit codes: 0 when culprits were found (or `check` passed), 1 when the full list did not
271
+ reproduce the error, the base game alone fails, the search was inconclusive, or `check`
272
+ failed, 2 for usage errors such as a missing install or RimWorld already running, 130
273
+ when interrupted.
274
+
275
+ ## Limitations
276
+
277
+ - Windows only, and only the Steam version has been tried. The probe is built for 1.6.
278
+ - It finds errors that happen while the game starts and during the first `--settle`
279
+ seconds on a fresh map. Problems that need your save, a raid or a year of play are out
280
+ of reach.
281
+ - Settings of mods that keep them outside `Config` (other than HugsLib) are not copied,
282
+ so those mods run with their defaults.
283
+ - A problem that depends on load order, rather than on which mods are present, is only
284
+ found as the set of mods involved. rimbisect does not try other orders.
285
+ - RimWorld has to be closed while it runs, and the machine is busy the whole time.
286
+
287
+ ## License
288
+
289
+ MIT