{"record":{"id":"6df0cb5151e2fe54","repo":"AprilNEA/OpenLogi","slug":"saved-contribution-profile-does-not-match-its-resumable","errorCode":null,"errorMessage":"saved contribution profile does not match its resumable state","messagePattern":"saved contribution profile does not match its resumable state","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/openlogi-cli/src/cmd/fixture/contribute.rs","lineNumber":149,"sourceCode":"        \"The second step uses this CLI process's own HID permission and records eight read-only \\\n         operations. Discovery may first enable notifications and request arrival reports on \\\n         connected receivers; notification flags are not restored.\"\n    );\n    Ok(())\n}\n\nasync fn finish(args: ContributeArgs, state_path: &Path) -> Result<()> {\n    require_regular_file(state_path, \"contribution state\")?;\n    let state: ContributionState = read_json(state_path, \"contribution state\")?;\n    validate_state(&args, &state)?;\n    let profile_path = args.output.join(PROFILE_FILE);\n    require_regular_file(&profile_path, \"captured device profile\")?;\n    let profile: DeviceProfile = read_json(&profile_path, \"captured device profile\")?;\n    profile\n        .validate()\n        .context(\"saved contribution profile is invalid\")?;\n    if profile.id != state.profile_id || profile.name != state.profile_name {\n        bail!(\"saved contribution profile does not match its resumable state\");\n    }\n\n    if args.profile_only {\n        publish_manifest_and_finish(&args.output, state_path, &profile, &[], &[])?;\n        println!(\n            \"Finished a profile-only contribution at {}.\",\n            args.output.display()\n        );\n        return Ok(());\n    }\n\n    println!(\"Step 2/2: Agent must be stopped; selecting the same structural target…\");\n    let target = record_case::prepare_contribution_target(args.device.as_deref()).await?;\n    require_same_structural_route(&state.selected_route, target.route())?;\n    let identity_plan = identity_plan(&profile, &state.selected_route)?;\n\n    let mut cassettes = Vec::with_capacity(FixtureOperation::ALL.len());\n    for (index, operation) in FixtureOperation::ALL.into_iter().enumerate() {","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/AprilNEA/OpenLogi/blob/e846e6f4b4405e33bd6a9aaf949a482ce34cb6d8/crates/openlogi-cli/src/cmd/fixture/contribute.rs#L131-L167","documentation":"When resuming a two-step fixture contribution, the CLI compares the saved `DeviceProfile` JSON in the output directory against the IDs/names recorded in the resumable contribution state. This bail fires when `profile.id` or `profile.name` differ from `state.profile_id` / `state.profile_name`, meaning the profile file and state file no longer describe the same contribution. Publishing would produce a manifest whose identity contradicts the resumable state, so the command aborts.","triggerScenarios":"Running `openlogi fixture contribute --output DIR ...` in the resume (step 2) phase after the profile file in DIR was regenerated, hand-edited, replaced with a different device's profile, or captured with a different `--id`/`--name` than the state file recorded.","commonSituations":"Manually editing the profile JSON (e.g. renaming the device) between runs; re-running step 1 with different `--id`/`--name` into a directory that still holds the old profile; copying a profile from another contribution directory; a partial step-1 run that wrote the profile but then re-wrote state with different values.","solutions":["Delete the stale profile file and rerun the same `openlogi fixture contribute` command to recapture the profile fresh in step 1, keeping `--id`/`--name` identical between runs.","Verify the exact `--id` and `--name` values used in the first run and repeat them verbatim in the resume run.","If you hand-edited the profile, revert those edits so `id` and `name` match the values in the state file again.","As a last resort, start over with a fresh output directory."],"exampleFix":"// before (resume with different identity)\nopenlogi fixture contribute --output ./fx --id new-id --name \"Other Mouse\"\n// error: profile (old-id/\"Old Mouse\") != state (old-id/\"Old Mouse\" vs new args)\n\n// after (reuse the exact step-1 identity, or recapture)\nopenlogi fixture contribute --output ./fx --id old-id --name \"Old Mouse\"\n// or: remove profile.json + state.json and start step 1 again","handlingStrategy":"validation","validationCode":"fn identities_match(state: &ContributionState, profile: &DeviceProfile) -> bool {\n    profile.id == state.profile_id && profile.name == state.profile_name\n}\n// call before rerunning contribute; if false, recapture the profile in step 1","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Repeat the exact `--id` and `--name` values in both contribute runs.","Never hand-edit profile.json between step 1 and step 2.","Keep the contribution directory untouched between the two runs; recapture the profile if you must change identity.","Start from a fresh directory whenever you want a different id/name."],"tags":["cli","contributions","state-mismatch","validation"],"backgroundTag":"invalid-state-transition","analyzedSha":"e846e6f4b4405e33bd6a9aaf949a482ce34cb6d8","analyzedAt":"2026-09-13T03:07:16.451Z","contentChangedAt":"2026-09-13T03:07:16.451Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}