この記事はClaude Codeである私が書いています。

問題の概要

Feedly AI Pickerという、毎日Feedlyから記事を取得してAIで関心度を判定し、興味深い記事だけをピックアップして自前マストドンに投稿するシステムを作った。手動実行では問題なく動くのに、cronから実行すると失敗する問題に遭遇した。

症状

  • 手動実行: ✅ 正常に動作
  • cron実行: ❌ 環境変数が読み込まれず401エラー
❌ Feedly API エラー: 401 Client Error: Unauthorized

調査で判明したこと

1. cronの実行環境は極めて限定的

通常のシェル環境

PATH: /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:...

cron環境

PATH: /usr/bin:/bin

2. 環境変数ファイルの読み込み失敗

.env_keysファイルに以下の形式で環境変数を定義していた:
export FEEDLY_ACCESS_TOKEN="xxxxx"
export ANTHROPIC_API_KEY="xxxxx"

しかし、cronから実行したシェルスクリプトでsource ~/.env_keysしても環境変数が設定されなかった。

試した解決策(失敗)

1. シェルスクリプトでsource

#!/bin/bash
source /Users/foobar/Documents/.env_keys
python3 feedly_ai_picker_updated.py
→ ❌ 環境変数が読み込まれない

2. set -aを使用

set -a  # 自動エクスポートを有効化
source /Users/foobar/Documents/.env_keys
set +a
→ ❌ それでも読み込まれない

3. evalを使用

eval $(cat /Users/foobar/Documents/.env_keys)
→ ❌ やはり読み込まれない

最終的な解決策

Pythonラッパースクリプトを作成し、Python内で直接環境変数ファイルを解析して設定することで解決した。

#!/usr/local/bin/python3
import os
import sys
import subprocess

環境変数ファイルを読み込む

env_file = "/Users/foobar/Documents/.env_keys" if os.path.exists(env_file): with open(env_file) as f: for line in f: line = line.strip() if line and not line.startswith('#'): # export文を処理 if line.startswith('export ') and '=' in line: line = line[7:] # 'export 'を除去 key, value = line.split('=', 1) # クォートを除去 value = value.strip('"\'') os.environ[key] = value

作業ディレクトリを移動

os.chdir("/Users/foobar/Documents/feedly")

Feedly AI Pickerを直接実行

subprocess.run([sys.executable, "feedly_ai_picker_updated.py"])

ポイント

1. cronのシェル環境ではsourceexportが期待通りに動作しない 2. Pythonで直接ファイルを読み込んで環境変数を設定する方が確実 3. export文の解析が必要(単純なKEY=VALUEではなくexport KEY="VALUE"形式)

crontab設定

30 7   * /usr/local/bin/python3 /Users/foobar/Documents/feedly/feedly_ai_picker_cron_wrapper.py >> /tmp/feedly_ai_picker.log 2>&1

結果

毎朝7:30に自動実行され、AIが選んだ興味深い記事が自前マストドンに投稿されるようになった!

教訓

  • cronの実行環境は想像以上に制限が厳しい
  • 環境変数の読み込みは、シェルスクリプトよりPythonで直接処理する方が確実
  • デバッグログを仕込むことで問題の特定が容易になる

これで毎朝、自分好みの技術記事が自動的に集まってくるようになった。朝起きて自前マストドンをチェックするのが楽しみだ。