Manfath / المدوّنة / EADDRINUSE على macOS

حلّ خطأ «المنفذ قيد الاستخدام» (EADDRINUSE) على macOS

تعطّل خادم التطوير، فأعدت تشغيله، فاستقبلك بـError: listen EADDRINUSE: address already in use :::3000. الخبر السار: المشكلة شكلية تقريباً دائماً. هنا سبب حدوثها، والطريقة الآمنة لتحرير المنفذ، وأمثلة عملية على البيئات الأكثر معاناةً منها.

ما يعنيه EADDRINUSE فعلاً

EADDRINUSE هو الخطأ الذي تُعيده النواة حين تستدعي عملية bind() على زوج عنوان+منفذ TCP/UDP محجوز سلفاً. ليس خطأً في منطق تطبيقك أبداً — بل النظام يرفض حجز المنفذ مرتين. سيناريوهان يُنتجان هذا الخطأ:

  1. عملية أخرى تستمع فعلاً على المنفذ.
  2. النسخة السابقة من عمليتك خرجت، لكن النواة لا تزال تعتبر المقبس مستخدماً (عادةً TIME_WAIT).

الحلّ مختلف في كل حالة.

الخطوة 1: عرف من يحتجز المنفذ

أسرع إجابة عبر lsof:

lsof -nP -iTCP:3000 -sTCP:LISTEN

ثلاث نتائج محتملة:

(للاطّلاع الأشمل على lsof، راجع إيجاد العملية المستخدمة لمنفذ على macOS.)

الخطوة 2: تحرير المنفذ — بهدوء

إن كان المتسبّب عمليتك أنت، أنهِها أوّلاً بـSIGTERM:

kill $(lsof -ti :3000)

SIGTERM هي الإشارة المهذّبة. تلتقطها العملية، تُغلق مقابسها، تُفرغ أيّ كتابات معلّقة، تحذف ملف الـPID، ثم تخرج. هذا ما تريد.

إن أعادت lsof -ti :3000 الـPID نفسه بعد ثوانٍ، صعِّد:

kill -9 $(lsof -ti :3000)

SIGKILL لا تُقاوَم — العملية لا تراها أصلاً. لذلك هي الملاذ الأخير: أيّ شيء كان يُفترض أن تنظّفه (ملفات قفل، مجلّدات مؤقّتة، مقابس في CLOSE_WAIT) سيبقى متروكاً.

لا تَلجأ تلقائياً إلى sudo kill -9. إن لم يستطع shell-ك إنهاء العملية، فالخطوة الصحيحة معرفة السبب — غالباً لأنها تعمل بمستخدم آخر. إضافة sudo تُفاقم مشكلة التنظيف.

الخطوة 3: لا شيء يستمع لكن المنفذ مشغول

تظهر EADDRINUSE، لكن lsof لا يُظهر شيئاً. هذه TIME_WAIT: مصافحة إغلاق TCP تُبقي المقبس متلكّئاً 30–120 ثانية لتُطابق الحزم المتأخّرة وتُسقطها بدلاً من تسليمها لخادم جديد على المنفذ نفسه.

أمامك ثلاثة خيارات:

يمكنك التأكّد من الحالة بـnetstat:

netstat -anv -p tcp | grep 3000

أسطر تنتهي بـTIME_WAIT هي السبب.

حرّر منفذاً دون كتابة.
يُظهِر Manfath كل منفذ مستمع في شريط القوائم مع إنهاء بنقرة واحدة — SIGTERM أوّلاً، SIGKILL فقط عند الضرورة. مجاني ومفتوح المصدر، بلا تتبّع.

أمثلة عملية

Node / Express / Next.js / Vite

Error: listen EADDRINUSE: address already in use :::3000

السبب: nodemon أو npm run dev أو تبويب آخر في طرفية أخرى لا يزال يحتجز المنفذ. الحلّ:

lsof -ti :3000 | xargs kill

في Next.js أو Vite، يمكنك أيضاً تمرير منفذ مختلف: PORT=3001 npm run dev أو vite --port 5174.

Rails / Puma

A server is already running. Check /tmp/pids/server.pid

يرفض Rails التشغيل إن كان ملف الـPID موجوداً. حلّان:

# 1. العملية ميتة، فقط ملف PID قديم
rm tmp/pids/server.pid

# 2. العملية حيّة، أنهِها أوّلاً
kill $(cat tmp/pids/server.pid)
rm tmp/pids/server.pid

Python / Flask / Django

OSError: [Errno 48] Address already in use

النمط نفسه: lsof -ti :8000 | xargs kill. خادم Django التطويري يفعّل SO_REUSEADDR عادةً، لذا هذه عملية متلكّئة حقيقية في الغالب — لا TIME_WAIT.

Docker

Error: Bind for 0.0.0.0:5432 failed: port is already allocated

نَكهتان:

الوقاية: ترك أشباح أقل


اقرأ بعد ذلك