You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Nodemailer+Localstack+Node AWS SDK发邮件遇多类错误求助

排查Nodemailer+Localstack+AWS SES SDK邮件发送错误

错误场景

  • 设置AWS_SES_ENDPOINT=localhost:4566
    Error: connect ECONNREFUSED 127.0.0.1:80
        at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1161:16)
    
  • 设置AWS_SES_ENDPOINT=127.0.0.1:4566
    uncaughtException: 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:4578
    Error: read ECONNRESET
        at TCP.onStreamRead (node:internal/stream_base_commons:220:20
    
  • 设置AWS_SES_ENDPOINT=http://localhost:4566或smtp://127.0.0.1:4566
    UnknownError
        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:4578
    Error [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格式不规范、端口与服务不匹配、协议选择错误。具体:

  1. AWS SDK v3的endpoint要求完整URL(必须带http:///https://),无协议前缀会默认走80端口,导致连接失败;
  2. Localstack配置中SES绑定的是4578端口,但AWS SDK调用SES用REST API协议,不能用SMTP协议前缀;
  3. 缺少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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 14:50:24