{"record":{"id":"d5421dc8e8aec1e2","repo":"TryGhost/Ghost","slug":"command-failed-with-exit-code-exitcode-comma","errorCode":null,"errorMessage":"Command failed with exit code ${exitCode}: ${command}\\nSTDOUT: ${stdout}\\nSTDERR: ${stderr}","messagePattern":"Command failed with exit code (.+?): (.+?)\\\\nSTDOUT: (.+?)\\\\nSTDERR: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"e2e/helpers/environment/service-managers/mysql-manager.ts","lineNumber":256,"sourceCode":"\n        // Use Docker modem's demuxStream to separate stdout and stderr\n        (container as ContainerWithModem).modem.demuxStream(stream, stdoutStream, stderrStream);\n\n        // Wait for the stream to end\n        await new Promise<void>((resolve, reject) => {\n            stream.on('end', () => resolve());\n            stream.on('error', reject);\n        });\n\n        // Get the exit code from exec inspection\n        const execInfo = await exec.inspect();\n        const exitCode = execInfo.ExitCode;\n\n        const stdout = Buffer.concat(stdoutChunks).toString('utf8').trim();\n        const stderr = Buffer.concat(stderrChunks).toString('utf8').trim();\n\n        if (exitCode !== 0) {\n            throw new Error(\n                `Command failed with exit code ${exitCode}: ${command}\\n` +\n                `STDOUT: ${stdout}\\n` +\n                `STDERR: ${stderr}`\n            );\n        }\n\n        return stdout;\n    }\n}\n","sourceCodeStart":238,"sourceCodeEnd":266,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/e2e/helpers/environment/service-managers/mysql-manager.ts#L238-L266","documentation":"MysqlManager runs a command inside the MySQL container via Docker exec, collects stdout/stderr streams, then inspects the exec for ExitCode. Any non-zero exit code produces this error embedding the command, stdout, and stderr. It's a thin wrapper surfacing the real failure of mysql/mysqladmin/sql inside the container (auth, syntax, missing DB, locked table).","triggerScenarios":"Running mysql -e with wrong credentials (MYSQL_ROOT_PASSWORD mismatch). Querying a database that doesn't exist. SQL syntax error in the command. Container busy / table locked. The exec targeted a DB user lacking privileges.","commonSituations":"Wrong root password env between manager and container; database name typo; reserved keyword unquoted; flapping container where exec runs mid-restart.","solutions":["Read STDERR in the message — the MySQL server error (access denied, unknown database, syntax) names the cause.","Verify the credentials passed to exec match the container's MYSQL_ROOT_PASSWORD / user grants.","Confirm the target database exists before running the command; create it if missing.","Quote identifiers and validate SQL syntax before exec."],"exampleFix":"// before: opaque exit-code failure\n\n// after: validate DB exists and credentials before exec\nconst dbs = await mysqlManager.exec('mysql -uroot -p$PW -e \"SHOW DATABASES\"');\nif (!dbs.includes(targetDb)) {\n    await mysqlManager.exec(`mysql -uroot -p$PW -e \"CREATE DATABASE \\`${targetDb}\\\"\"`);\n}","handlingStrategy":"try-catch","validationCode":"async function dbExists(m: MysqlManager, name: string): Promise<boolean> {\n    const out = await m.exec(`mysql -uroot -p${PW} -e \"SHOW DATABASES\"`);\n    return out.split('\\n').some(l => l === name);\n}","typeGuard":null,"tryCatchPattern":"try {\n    return await mysqlManager.exec(command);\n} catch (e) {\n    // message already embeds command/stdout/stderr — rethrow with cause for upstream context\n    throw new Error(`mysql exec failed: ${(e as Error).message}`, {cause: e});\n}","preventionTips":["Validate the target DB exists and credentials are correct before running commands.","Quote SQL identifiers and parameterize values to avoid syntax errors.","Keep the manager's root password env in sync with the container's MYSQL_ROOT_PASSWORD."],"tags":["mysql","docker","e2e","exec","credentials"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}