Skip to content

Bambu FTPS uploads fail with "Premature close" — printer requires TLS session reuse; basic-ftp doesn't negotiate it #22

Description

@Lickitysplitted

Symptom

print_3mf (and upload_gcode) against a Bambu X1C consistently fail during the FTPS upload step:

Error: Failed to start print: Premature close

MQTT works fine (status, commands); only the file upload leg fails. Retries never succeed.

Environment: mcp-3d-printer-server 1.2.5 (npm), Node 22, Ubuntu 24.04 container, X1C on firmware 01.08.02.00, LAN mode, implicit FTPS :990.

Root cause

Bambu's FTPS server requires TLS session reuse between the control and data channels. basic-ftp (used by ftpUpload in src/printers/bambu.ts) does not resume the control-channel TLS session on the data connection, so the printer drops the data socket — surfacing as Premature close.

This is the same root cause as the 522 SSL connection failed: session reuse required failures documented in your sibling project bambu-printer-mcp (there hit by a Go FTPS client).

Verification

From the same container, against the same printer, in the same session where print_3mf failed:

curl -sS --insecure --ssl-reqd -u "bblp:$ACCESS_CODE" -T plate.3mf "ftps://$PRINTER_IP:990/file.3mf"

succeeds every time (curl negotiates TLS session reuse), and a subsequent MQTT project_file command started a real print. So credentials, network, and firmware are fine — only the basic-ftp upload path fails.

Workaround / reference fix

We patch ftpUpload (dist) at Docker-image build time to shell out to curl — running in production against the X1C:

async ftpUpload(host, token, localPath, remotePath) {
    // curl negotiates the TLS session reuse Bambu's FTPS server requires
    const { execFile } = await import("node:child_process");
    const absoluteRemote = remotePath.startsWith("/") ? remotePath : `/${remotePath}`;
    await new Promise((resolve, reject) => {
        execFile("curl", ["-sS", "--insecure", "--ssl-reqd", "--ftp-create-dirs", "-u", `bblp:${token}`, "-T", localPath, `ftps://${host}:990${absoluteRemote}`], { timeout: 300000 }, (error, stdout, stderr) => {
            if (error) reject(new Error(`curl FTPS upload failed: ${stderr || error.message}`));
            else resolve(undefined);
        });
    });
}

Possible proper fixes

  • Pass the control socket's TLS session to the data connection (tls.connect session option) — basic-ftp exposes hooks that may allow this, or it may need an upstream basic-ftp change.
  • Or adopt the curl/execFile approach when curl is available, as bambu-printer-mcp did with its client swap.

Happy to test a fix against the live X1C.

🤖 Filed with Claude Code on behalf of the reporter.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions