[GH-ISSUE #1204] [Bug]: Archive created with wrong plate metadata when consecutive plates of the same model are printed #872

Closed
opened 2026-05-06 12:33:34 +02:00 by BreizhHardware · 2 comments

Originally created by @BurntOutHylian on GitHub (May 3, 2026).
Original GitHub issue: https://github.com/maziggy/bambuddy/issues/1204

Originally assigned to: @maziggy on GitHub.

Component

Bambuddy

Bug Description

When two different plates of the same model are printed back-to-back, the second print's archive can be created using the first plate's 3MF metadata.

At the moment Bambuddy detects the print_start MQTT event, the printer's MQTT state (subtask_name / filename) may still reflect the previous plate's job.

Bambuddy downloads the wrong 3MF from the printer's FTP and creates the archive with that file's metadata (wrong print name, slicer time estimate, and filament slot data) all locked in at row creation. There is no follow-up to correct.

Diagnosis in Support package section

Expected Behavior

Each archive should reflect its actual plate: correct name (e.g. MyModel - Plate 1, not MyModel - Plate 2), correct slicer estimate, and correct per-slot filament data.

Steps to Reproduce

With a print/printer

  1. Print Plate 2 of any multi-plate model and wait for it to fully complete
  2. Immediately start Plate 1 of the same model from the same project
  3. Let Plate 1 complete (or check the archive shortly after it starts)
  4. The resulting archive will be named "…Plate 2" and show Plate 2's filament slots and time estimate

Via live DB Data

  1. Run this query:
python3 -c "
import sqlite3, glob, json
con = sqlite3.connect(glob.glob('/app/data/*.db')[0]) # your db location/path here
rows = con.execute('''
  SELECT id, print_name, print_time_seconds,
    CAST((julianday(completed_at)-julianday(started_at))*86400 AS INTEGER) AS actual_sec,
    filament_used_grams, extra_data
  FROM print_archives WHERE status=\"completed\"
  ORDER BY completed_at DESC LIMIT 10
''').fetchall()
for r in rows:
    ed = json.loads(r[5]) if r[5] else {}
    slots = ed.get('filament_slots', [])
    print(f'id={r[0]} name={r[1]} stored_sec={r[2]} actual_sec={r[3]} filament={r[4]}g slots={slots}')
  1. If affected: print_name contains a plate number inconsistent with the actual print, filament_slots reflects the wrong plate.

Printer Model

P2S

Bambuddy Version

0.2.3.2, dev branch

SpoolBuddy Version

No response

Printer Firmware Version

01.02.00.00

Installation Method

Docker

Operating System

Docker

Relevant Logs / Support Package

Diagnosis

When Bambuddy detects a print start via MQTT, it immediately reads subtask_name and filename from the MQTT payload to identify and download the 3MF from the printer's FTP. There is no check that this data belongs to the newly-started job.

If the printer's MQTT state hasn't transitioned yet (still reflecting the previous job), Bambuddy downloads the wrong plate's 3MF. The archive is created from that file's metadata.

Suggestion/Thoughts

Simply not trusting subtask_name doesn't help unless there's an alternative source of truth to validate against.
The filename field from MQTT (e.g., /data/Metadata/plate_1.gcode) contains the plate number explicitly, and is plate-specific, but subtask_name is model-level and can lag. The 3MF parser also extracts _plate_index from the parsed file, so after download you can cross-check: "the file I downloaded claims to be Plate 2, but filename says I'm printing plate 1."

I think there's 2 viable options here, but tbh I'm still understanding the 3MF parsing portion of Bambuddy.

Step 1, Detect
After parsing the downloaded 3MF, extract the expected plate number from filename via regex (plate_(\d+)) and compare it against the _plate_index the parser read from the file. A mismatch would identify that the wrong 3MF was used

Step 2, Correct the wrong 3MF use
This is where I'm still learning. I can think of 2 options here:

Option A: Re-download using filename
On mismatch, drop subtask_name and retry the FTP download using the plate-specific path derived from filename (e.g., plate_1.gcode.3mf). This fix has a higher-fidelity, but ends up adding an extra FTP round-trip on affected prints. It also assumes the plate-specific file is present on the printer's FTP.

Option B: Fall back to no-3MF archive
On mismatch, discard the parsed metadata and create the archive without 3MF data (the existing fallback path at [main.py:2154] already handles this). The archive name would fall back to the raw filename stem, which at least reflects the correct plate. This is easier, but ends up losing the slicer estimates/metadata

This assumes filename from MQTT is reliably plate-specific and not also stale. That's probably worth verifying. If both subtask_name and filename lag together, Option A's re-download would still target the wrong file.

Screenshots

No response

Additional Context

No response

Checklist

  • I have searched existing issues to ensure this bug hasn't already been reported
  • I am using the latest version of Bambuddy
  • My printer is set to LAN Only mode
  • My printer has Developer Mode enabled
Originally created by @BurntOutHylian on GitHub (May 3, 2026). Original GitHub issue: https://github.com/maziggy/bambuddy/issues/1204 Originally assigned to: @maziggy on GitHub. ### Component Bambuddy ### Bug Description When two different plates of the same model are printed back-to-back, the second print's archive can be created using the first plate's 3MF metadata. At the moment Bambuddy detects the print_start MQTT event, the printer's MQTT state (subtask_name / filename) may still reflect the previous plate's job. Bambuddy downloads the wrong 3MF from the printer's FTP and creates the archive with that file's metadata (wrong print name, slicer time estimate, and filament slot data) all locked in at row creation. There is no follow-up to correct. Diagnosis in Support package section ### Expected Behavior Each archive should reflect its actual plate: correct name (e.g. MyModel - Plate 1, not MyModel - Plate 2), correct slicer estimate, and correct per-slot filament data. ### Steps to Reproduce #### With a print/printer 1. Print Plate 2 of any multi-plate model and wait for it to fully complete 2. Immediately start Plate 1 of the same model from the same project 3. Let Plate 1 complete (or check the archive shortly after it starts) 4. The resulting archive will be named "…Plate 2" and show Plate 2's filament slots and time estimate #### Via live DB Data 1. Run this query: ``` python3 -c " import sqlite3, glob, json con = sqlite3.connect(glob.glob('/app/data/*.db')[0]) # your db location/path here rows = con.execute(''' SELECT id, print_name, print_time_seconds, CAST((julianday(completed_at)-julianday(started_at))*86400 AS INTEGER) AS actual_sec, filament_used_grams, extra_data FROM print_archives WHERE status=\"completed\" ORDER BY completed_at DESC LIMIT 10 ''').fetchall() for r in rows: ed = json.loads(r[5]) if r[5] else {} slots = ed.get('filament_slots', []) print(f'id={r[0]} name={r[1]} stored_sec={r[2]} actual_sec={r[3]} filament={r[4]}g slots={slots}') ``` 2. If affected: print_name contains a plate number inconsistent with the actual print, filament_slots reflects the wrong plate. ### Printer Model P2S ### Bambuddy Version 0.2.3.2, dev branch ### SpoolBuddy Version _No response_ ### Printer Firmware Version 01.02.00.00 ### Installation Method Docker ### Operating System Docker ### Relevant Logs / Support Package #### Diagnosis When Bambuddy detects a print start via MQTT, it immediately reads subtask_name and filename from the MQTT payload to identify and download the 3MF from the printer's FTP. There is no check that this data belongs to the newly-started job. If the printer's MQTT state hasn't transitioned yet (still reflecting the previous job), Bambuddy downloads the wrong plate's 3MF. The archive is created from that file's metadata. #### Suggestion/Thoughts Simply not trusting subtask_name doesn't help unless there's an alternative source of truth to validate against. The filename field from MQTT (e.g., /data/Metadata/plate_1.gcode) contains the plate number explicitly, and is plate-specific, but subtask_name is model-level and can lag. The 3MF parser also extracts _plate_index from the parsed file, so after download you can cross-check: "the file I downloaded claims to be Plate 2, but filename says I'm printing plate 1." I think there's 2 viable options here, but tbh I'm still understanding the 3MF parsing portion of Bambuddy. **Step 1, Detect** After parsing the downloaded 3MF, extract the expected plate number from filename via regex (plate_(\d+)) and compare it against the _plate_index the parser read from the file. A mismatch would identify that the wrong 3MF was used **Step 2, Correct the wrong 3MF use** This is where I'm still learning. I can think of 2 options here: _Option A: Re-download using filename_ On mismatch, drop subtask_name and retry the FTP download using the plate-specific path derived from filename (e.g., plate_1.gcode.3mf). This fix has a higher-fidelity, but ends up adding an extra FTP round-trip on affected prints. It also assumes the plate-specific file is present on the printer's FTP. _Option B: Fall back to no-3MF archive_ On mismatch, discard the parsed metadata and create the archive without 3MF data (the existing fallback path at [main.py:2154] already handles this). The archive name would fall back to the raw filename stem, which at least reflects the correct plate. This is easier, but ends up losing the slicer estimates/metadata This assumes filename from MQTT is reliably plate-specific and not also stale. That's probably worth verifying. If both subtask_name and filename lag together, Option A's re-download would still target the wrong file. ### Screenshots _No response_ ### Additional Context _No response_ ### Checklist - [x] I have searched existing issues to ensure this bug hasn't already been reported - [x] I am using the latest version of Bambuddy - [x] My printer is set to LAN Only mode - [x] My printer has Developer Mode enabled
BreizhHardware 2026-05-06 12:33:34 +02:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@maziggy commented on GitHub (May 4, 2026):

Thanks for the very thorough diagnosis — your analysis was correct end-to-end and pointed me right at the missing check.

Confirmed the flow:

  • The print_start callback fires when MQTT gcode_file changes (bambu_mqtt.py:2781-2786). That field is plate-specific and always fresh — it's the field whose change triggered the event.
  • subtask_name is model-level and can still echo the previous job in the same MQTT batch.
  • main.py:1974 ordered FTP candidates with subtask_name first, so the previous Plate 2 upload still resident on the printer's FTP got picked. The 3MF parser then locked Plate 2's _plate_index / name / time / per-slot filaments into the row at creation.
  • archive.py:996-1006 already extracts plate_number from print_data["filename"] and passes it to the parser as a hint, but archive.py:154 re-reads _plate_index from inside the file. Those two values were never compared.

Going with your Option A as the primary fix and Option B (no-3MF fallback) only when the corrected re-download also can't find a matching file. Concretely:

  1. New peek_plate_index_in_3mf(path) reads only Metadata/slice_info.config to grab the plate index cheaply.
  2. New swap_plate_suffix(name, target_plate) rewrites the trailing plate number — handles both the spaced "Project - Plate N" form and the underscored "plate_N" form (the underscored variant shows up in real subtask_names — Box3.0(2)_plate_5 is in the existing test fixtures).
  3. After the FTP download in _handle_print_start, the new validation block compares peek_plate_index_in_3mf(temp_path) against parse_plate_id(filename). On mismatch, it retries FTP with the corrected subtask_name and accepts the result only if the retry's plate also matches.
  4. If retry fails (no matching file on FTP, or no recognisable plate suffix to swap), the wrong 3MF is dropped and the existing no-3MF fallback at main.py:2155 creates the archive — with the stale subtask_name overridden so the fallback print_name reflects the right plate rather than locking in a misleading one.

Validation only runs when parse_plate_id(filename) returns a value, so single-plate / cloud-named / non-Bambu jobs are unaffected.

Your hypothesis about filename not lagging together with subtask_name is the load-bearing assumption — if both lag, the retry will fail and the no-3MF fallback kicks in, so the archive name still reflects the right plate (derived from the fresh gcode_file path). Worst case: missing slicer estimate / per-slot filaments on the affected print, never wrong-plate metadata silently locked in.

Available/Fixed in branch dev and available with the next release or daily build. Please let me know if it works for you.


If you find Bambuddy useful, please consider giving it a on GitHub — it helps others discover the project!

<!-- gh-comment-id:4369275718 --> @maziggy commented on GitHub (May 4, 2026): Thanks for the very thorough diagnosis — your analysis was correct end-to-end and pointed me right at the missing check. Confirmed the flow: - The print_start callback fires when MQTT gcode_file changes (bambu_mqtt.py:2781-2786). That field is plate-specific and always fresh — it's the field whose change triggered the event. - subtask_name is model-level and can still echo the previous job in the same MQTT batch. - main.py:1974 ordered FTP candidates with subtask_name first, so the previous Plate 2 upload still resident on the printer's FTP got picked. The 3MF parser then locked Plate 2's _plate_index / name / time / per-slot filaments into the row at creation. - archive.py:996-1006 already extracts plate_number from print_data["filename"] and passes it to the parser as a hint, but archive.py:154 re-reads _plate_index from inside the file. Those two values were never compared. Going with your Option A as the primary fix and Option B (no-3MF fallback) only when the corrected re-download also can't find a matching file. Concretely: 1. New peek_plate_index_in_3mf(path) reads only Metadata/slice_info.config to grab the plate index cheaply. 2. New swap_plate_suffix(name, target_plate) rewrites the trailing plate number — handles both the spaced "Project - Plate N" form and the underscored "_plate_N" form (the underscored variant shows up in real subtask_names — Box3.0_(2)_plate_5 is in the existing test fixtures). 3. After the FTP download in _handle_print_start, the new validation block compares peek_plate_index_in_3mf(temp_path) against parse_plate_id(filename). On mismatch, it retries FTP with the corrected subtask_name and accepts the result only if the retry's plate also matches. 4. If retry fails (no matching file on FTP, or no recognisable plate suffix to swap), the wrong 3MF is dropped and the existing no-3MF fallback at main.py:2155 creates the archive — with the stale subtask_name overridden so the fallback print_name reflects the right plate rather than locking in a misleading one. Validation only runs when parse_plate_id(filename) returns a value, so single-plate / cloud-named / non-Bambu jobs are unaffected. Your hypothesis about filename not lagging together with subtask_name is the load-bearing assumption — if both lag, the retry will fail and the no-3MF fallback kicks in, so the archive name still reflects the right plate (derived from the fresh gcode_file path). Worst case: missing slicer estimate / per-slot filaments on the affected print, never wrong-plate metadata silently locked in. Available/Fixed in branch dev and available with the next release or daily build. Please let me know if it works for you. ----- If you find Bambuddy useful, please consider giving it a ⭐ on [GitHub](https://github.com/maziggy/bambuddy) — it helps others discover the project!
Author
Owner

@BurntOutHylian commented on GitHub (May 4, 2026):

Confirmed. Nice! And super quick turnaround

<!-- gh-comment-id:4371031313 --> @BurntOutHylian commented on GitHub (May 4, 2026): Confirmed. Nice! And super quick turnaround
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/bambuddy#872
No description provided.