0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AWS SAM】iOS実機↔︎SAMの通信で "Bad file descriptor" エラーが出た時に見る記事

Last updated at Posted at 2025-04-06

はじめに

AWS SAMで1日半ハマり激萎え、そして解決。その備忘録です。
結構ピンポイントの問題に関する記事ですが、同じハマり方をする人は結構居そうだと思ったので投稿します。

対象読者

  • AWS SAM(Serverless Application Model)を使ってAPIを開発している方
  • XcodeでiOS (watchOS, visionOS) 開発をしている方
  • 上記のiOSなどの実機から、ローカルのSAMのAPIにリクエストを送る設計のアプリをつくっt

環境

ツール バージョン
MacOS 15.0.1
Xcode 16.1
AWS SAM CLI(Runtime: Java21) 1.135.0
Python 3.13
Colima 0.7.5

筆者のアプリの構造

認証などは端折って書いていますが、おおまかにこのような構造です
image.png

ローカル開発は、このように実施します

  • クライアント:iPhone実機
  • API:AWS SAMでAPI GatewayとLambdaをローカル再現
  • DB: Dynamodb-localのimageを使ってコンテナを立ち上げ

image.png

起こった現象

状況を再現します。

1. SAMの起動

下記コマンドでSAMを起動しました
※docker containerで動いているdynamoDBに接続するために、docker netowrkを指定していますが、今回はあまり気にしなくていいです

sam local start-api --host 0.0.0.0 --docker-network dynamo-network

起動すると下記のようなログが出力されます

# ~~ 一部省略 ~~
Containers Initialization is done.

Mounting GetCurrentVersion at http://127.0.0.1:3000/api/version [GET, OPTIONS]

You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. If you used sam build before     
running local commands, you will need to re-run sam build for the changes to be picked up. You only need to restart SAM CLI if you update your AWS SAM template

 * * Tip: There are .env or .flaskenv files present. Do "pip install python-dotenv" to use them.
2025-04-06 14:41:24 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:3000
 * Running on http://xxx.xxx.xxx.xxx:3000

2025-04-06 14:41:24 Press CTRL+C to quit

http://127.0.0.1:3000http://xxx.xxx.xxx.xxx:3000
でapiがホストされました
/api/versionというGETのエンドポイントを一つ実装している前提です。

SAMの起動で重要なところ

--host 0.0.0.0

このオプションは、SAM CLI を使って iPhone 実機や他の端末からアクセスするために 必須です。

  • SAM CLIは、デフォルトではlocalhost(=127.0.0.1)だけにバインドされます
  • つまり、Mac上のアプリやブラウザからしかアクセスできません
  • 同じWi-Fiの iPhone 実機や別PCからのリクエストは届きません

これを忘れると、実機iPhoneからのリクエストは、SAMに一生届きません。

【ログの比較】

# ❌ --host オプションなしで起動(デフォルト)
sam local start-api

# localhost のみバインド
> * Running on http://127.0.0.1:3000
# ✅ --host 0.0.0.0 を指定して起動
sam local start-api --host 0.0.0.0

# 全てのインターフェースにバインドされる
> * Running on all addresses (0.0.0.0)
> * Running on http://127.0.0.1:3000
> * Running on http://xxx.xxx.xxx.xxx:3000 ← MacのローカルIPもOK!

2. ブラウザからAPIを叩いてみる

http://localhost:3000/api/version
ブラウザから叩いてみると、正常にレスポンスが返ってきます✅
順調、順調。

3. iOS実機からAPIを叩いてみる

以下のような実装で、iOSからSAMにリクエストを送ります
かなり端折っていますが、要は該当API /api/version にGETリクエストを送っています。

let urlRequest = URLRequest(url: URL("http://xxx.xxx.xxx.xxx:3000/api/version")!)
try await URLSession.shared.data(for: urlRequest)

iOSの実装で重要なところ
urlをhttp://localhost:3000とせずに、
http://xxx.xxx.xxx.xxx:3000としているのは、
iPhone実機からlocalhostにリクエストを送ると、MacではなくiPhone自体のlocalhostを参照してしまうからです。

xxx.xxx.xxx.xxxに入る値は、
MacがWi-Fiルーターから取得したプライベートIPアドレスです。
iPhone実機などと同じWi-Fiに接続していれば、LAN内通信に使えるIPとなります。
下記コマンドで取得できます。

ipconfig getifaddr en0

> xxx.xxx.xxx.xxx # MacのプライベートIP

出力されたエラー

iOSからのリクエストが失敗しました。❌

iOS側

in handle error: Error Domain=NSURLErrorDomain Code=-1005 "The network connection was lost." UserInfo={_kCFStreamErrorCodeKey=-4, NSUnderlyingError=0x303535b60 {Error Domain=kCFErrorDomainCFNetwork Code=-1005 "(null)" UserInfo={NSErrorPeerAddressKey=<CFData 0x3018090e0 [0x1ffb73830]>{length = 16, capacity = 16, bytes = 0x10020bb8a9fef85f0000000000000000}, _kCFStreamErrorCodeKey=-4, _kCFStreamErrorDomainKey=4}}, _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <171B3E0B-1287-405E-8255-25E16D8A3EF0>.<1>, _NSURLErrorRelatedURLSessionTaskErrorKey=(
    "LocalDataTask <171B3E0B-1287-405E-8255-25E16D8A3EF0>.<1>"
), NSLocalizedDescription=The network connection was lost., NSErrorFailingURLStringKey=http://xxx.xxx.xxx.xxx:3000/api/version, NSErrorFailingURLKey=http://xxx.xxx.xxx.xxx:3000/api/version, _kCFStreamErrorDomainKey=4}

SAM側

----------------------------------------
Exception occurred during processing of request from ('xxx.xxx.xxx.xxx', 64319)
----------------------------------------
Exception occurred during processing of request from ('xxx.xxx.xxx.xxx', 64320)
----------------------------------------
Exception occurred during processing of request from ('xxx.xxx.xxx.xxx', 64321)
Traceback (most recent call last):
Traceback (most recent call last):
----------------------------------------
Exception occurred during processing of request from ('xxx.xxx.xxx.xxx', 64322)

Traceback (most recent call last):
  File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/socketserver.py", line 697, in process_request_thread
    self.finish_request(request, client_address)
    ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/socketserver.py", line 362, in finish_request
    self.RequestHandlerClass(request, client_address, self)
    ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/socketserver.py", line 766, in __init__
    self.handle()
    ~~~~~~~~~~~^^

# ~~ 一部省略 ~~

Traceback (most recent call last):
  File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/socketserver.py", line 362, in finish_request
    self.RequestHandlerClass(request, client_address, self)
    ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
----------------------------------------
  File "/opt/homebrew/Cellar/python@3.13/3.13.2/Frameworks/Python.framework/Versions/3.13/lib/python3.13/socket.py", line 719, in readinto
    return self._sock.recv_into(b)
           ~~~~~~~~~~~~~~~~~~~~^^^
OSError: [Errno 9] Bad file descriptor

# ~~ 一部省略 ~~

OSError: [Errno 57] Socket is not connected
    

OSError: [Errno 9] Bad file descriptor この部分が怪しい。
全体的に、SAMのAPIのPython製エミュレータ(Werkzeug + socketserver)がクラッシュしている感じです。

原因

結論、
SAM CLI v1.135.0(=Python 3.13 + Werkzeug)に含まれる
「開発用HTTPサーバーの不具合」っぽいです。

つまりSAMの新しいバージョンでは、
「SAM CLIが内部で動かしている簡易Webサーバー」が調子悪いみたいです。

SAM CLI ver Python Werkzeug 状態
✅ v1.128.0 3.11 2.2.x〜2.3.x 安定
❌ v1.129.0〜v1.135.0 3.12〜3.13 2.3.x以降 不安定(Bad file descriptor)

やったこと

SAM CLI のバージョンを 1.135.01.128.0に ダウングレードしました。
1日半あれこれハマりましたが、最終的にはそれだけで解消しました。それだけで。

おわりに

こういう依存関係でハマるのが一番しんどいですね。

iOS × SAM で開発されている方はご注意を!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?