使用Nodemailer+Localstack+Node AWS SDK发邮件遇多类错误求助
排查Nodemailer+Localstack+AWS SES SDK邮件发送错误
错误场景
- 设置
AWS_SES_ENDPOINT=localhost:4566Error: connect ECONNREFUSED 127.0.0.1:80 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1161:16) - 设置
AWS_SES_ENDPOINT=127.0.0.1:4566uncaughtException: Invalid URL TypeError [ERR_INVALID_URL]: Invalid URL at new NodeError (node:internal/errors:371:5) at onParseError (node:internal/url:552:9) at new URL (node:internal/url:628:5) at parseUrl (/something/node_modules/@aws-sdk/url-parser/dist-cjs/index.js:6:60) at resolveEndpointsConfig (/something/node_modules/@aws-sdk/config-resolver/dist-cjs/endpointsConfig/resolveEndpointsConfig.js - 设置
AWS_SES_ENDPOINT=http://localhost:4578或http://127.0.0.1:4578Error: read ECONNRESET at TCP.onStreamRead (node:internal/stream_base_commons:220:20 - 设置
AWS_SES_ENDPOINT=http://localhost:4566或smtp://127.0.0.1:4566UnknownError at deserializeAws_querySendRawEmailCommandError (/something/node_modules/@aws-sdk/client-ses/dist-cjs/protocols/Aws_query.js:2779:24) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async /something/node_modules/@aws-sdk/middleware-serde/dist-cjs/deserializerMiddleware.js:7:24 at async /something/node_modules/@aws-sdk/middleware-signing/dist-cjs/middleware.js:11:20 at async StandardRetryStrategy.retry (/something/node_modules/@aws-sdk/middleware-retry/dist-cjs/StandardRetryStrategy.js:51:46) at async /something/node_modules/@aws-sdk/middleware-logger/dist-cjs/loggerMiddleware.js:6:22 - 设置
AWS_SES_ENDPOINT=smtp://127.0.0.1:4578Error [TimeoutError]: socket hang up at connResetException (node:internal/errors:691:14) at Socket.socketOnEnd (node:_http_client:471:23) at Socket.emit (node:events:402:35) at endReadableNT (node:internal/streams/readable:1343:12) at processTicksAndRejections (node:internal/process/task_queues:83:21) { code: 'ECONNRESET',
相关配置
SES客户端配置
const sesConfig: SESClientConfig = { region: "us-east-1", endpoint: AWS_SES_ENDPOINT, }; const ses = new SES(sesConfig);
Localstack Docker Compose配置
services: qatool-localstack: container_name: "my-localstack" image: localstack/localstack ports: - "127.0.0.1:4566:4566" - "127.0.0.1:4572:4572" - "127.0.0.1:4578:4578" environment: - SERVICES=sqs:4566,s3:4572,ses:4578 - DEFAULT_REGION=us-east-1 - DATA_DIR=${TMPDIR:-/tmp/}localstack/data - HOST_TMP_FOLDER=${TMPDIR:-/tmp/}localstack volumes: - "${TMPDIR:-/tmp}/localstack:/tmp/localstack" - "/var/run/docker.sock:/var/run/docker.sock" - './localstackSetup.sh:/docker-entrypoint-initaws.d/make-s3.sh'
依赖版本
"@aws-sdk/client-ses": "^3.110.0", "nodemailer": "^6.7.5",
问题排查与修复
核心问题分析
错误集中在三点:端点URL格式不规范、端口与服务不匹配、协议选择错误。具体:
- AWS SDK v3的
endpoint要求完整URL(必须带http:///https://),无协议前缀会默认走80端口,导致连接失败; - Localstack配置中SES绑定的是4578端口,但AWS SDK调用SES用REST API协议,不能用SMTP协议前缀;
- 缺少Localstack适配的SDK配置(如测试密钥、路径风格)。
分步修复
1. 修正端点URL格式
将环境变量设置为带HTTP协议的正确端口:
AWS_SES_ENDPOINT=http://localhost:4578
原因:SDK v3必须用完整URL,且Localstack的SES REST API端口为4578(与docker-compose配置匹配)。
2. 优化Localstack配置(可选)
Localstack v0.12+推荐统一通过4566端口访问所有服务,可简化SERVICES配置:
environment: - SERVICES=sqs,s3,ses - DEFAULT_REGION=us-east-1 # 保留其他环境变量
此时端点可改为http://localhost:4566,Localstack会自动路由到SES服务。
3. 补充SDK适配配置
AWS SDK连接Localstack时,需禁用签名验证并添加测试密钥:
const sesConfig: SESClientConfig = { region: "us-east-1", endpoint: AWS_SES_ENDPOINT, credentials: { accessKeyId: "test", secretAccessKey: "test" }, forcePathStyle: true }; const ses = new SES(sesConfig);
原因:Localstack不校验真实AWS密钥,但SDK默认会尝试签名请求,需显式配置测试密钥并适配路径风格。
4. 验证SES服务状态
启动Localstack后,用AWS CLI验证服务是否正常:
aws ses list-identities --endpoint-url http://localhost:4578 --region us-east-1
若返回正常结果,说明SES服务启动成功;若报错,检查docker-compose的SERVICES配置或初始化脚本是否影响服务启动。
各错误对应修复说明
- ECONNREFUSED 127.0.0.1:80:端点无协议前缀,SDK默认走80端口,添加
http://前缀即可解决; - ERR_INVALID_URL:端点不是完整URL格式,必须包含
http://; - ECONNRESET:确认Localstack的SES服务是否正常启动,检查端口占用;
- UnknownError:用了错误的端口/协议,改用对应REST API端口和
http://前缀; - socket hang up:用SMTP协议访问REST API端口,协议不匹配,改用
http://前缀。
内容的提问来源于stack exchange,提问作者user1689987
相关产品推荐
相关产品推荐

