الرمز كان صالحاً، ومع ذلك واجه العميل المستقل (Headless Agent) خطأ 401.

قمت ببناء أداتين صغيرتين لاستدعاء Claude عبر الكود. إحداهما تُنشئ رسائل الالتزام (commit messages)، والأخرى تُحدث الملفات الشخصية.

بدا الكود مثالياً. فقد استخرج مفتاح API من البيئة، وأرسل طلب HTTPS خام إلى Anthropic API، ثم قام بتحليل استجابة JSON.

لكنه فشل في كل مرة بخطأ 401.

لم تكن المشكلة في الكود، بل كانت في طريقة المصادقة (authentication method).

أنا لا أستخدم مفتاح API خاماً، بل أستخدم Claude Code عبر اشتراك. وهذا يستخدم OAuth من خلال Claude CLI، وهو نظام مختلف عن النظام الذي توقعه السكربت الخاص بي.

كانت بيئتي تحتوي على قيمة قديمة أو فارغة تحت متغير مفتاح API. رأى السكربت قيمة واعتقد أنها صالحة، فخرج الطلب بنوع خاطئ من بيانات الاعتماد (credentials).

كان الحل بسيطاً؛ توقفت عن إجراء استدعاءات API مباشرة، وبدلاً من ذلك، أمرت السكربت باستخدام الـ CLI الذي يمتلك بالفعل جلسة (session) صالحة.

الطريقة القديمة: استخدام urllib لإرسال طلب باستخدام مفتاح API.

الطريقة الجديدة: استخدام subprocess لتشغيل أمر "claude" مباشرة.

يتولى الـ CLI إدارة الجلسة، والرموز (tokens)، ومدة الصلاحية نيابة عنك.

هذا الخطأ خطير في البيئات المستقلة (headless environments). فالبشر يرون الخطأ في الطرفية (terminal)، أما مهام cron أو خطوط أنابيب CI فتفشل بصمت، وقد لا تلاحظ ذلك لعدة أيام.

ستلاحظ هذا النمط نفسه في أماكن أخرى:

  • أداة تبحث عن رمز وصول شخصي (personal access token) بينما يستخدم النظام OIDC.
  • سكربت يقرأ مفاتيح AWS بينما يستخدم النظام دور IAM (IAM role).

الرمز موجود، وصيغته صحيحة، ويجتاز الفحوصات الأساسية، لكنه يستخدم الآلية الخاطئة.

اتبع هذه القواعد لتجنب ذلك:

  • استفسر عن كيفية مصادقة بيئتك بشكل تفاعلي قبل كتابة الكود، ولا تكتفِ باتباع وثائق الـ API فقط.
  • لا تثق بـ os.environ؛ فقد تكون القيمة الموجودة قديمة أو خاطئة.
  • إذا كانت أداة CLI تتولى المصادقة نيابة عنك، فاستخدمها. قم باستدعاء الـ CLI بدلاً من إعادة بناء منطق المصادقة بنفسك.
  • إذا واجهت خطأ 401، فافحص مسار بيانات الاعتماد (credential path) قبل تغيير كود الطلب الخاص بك.

المصدر: https://dev.to/enjoy_kumawat/the-token-was-valid-my-headless-agent-401d-anyway-3bgl

مجتمع تعليمي اختياري: https://t.me/GyaanSetuAi