mcpField
F
frappe-errors-clientscripts

frappe-errors-clientscripts

github.com/impertio-studio開発198110 回インストール30 週間前に更新
リポジトリ

frappe-errors-clientscripts について

Frappe のクライアントスクリプト向けトラブルシューティングガイドです。よくあるエラーの原因と修正方法を対応付け、非同期呼び出し、子テーブル、イベントタイミング、ブラウザーでのデバッグを扱います。

主な機能

  1. 01エラーメッセージから原因を特定
  2. 02よくある問題の修正パターン
  3. 03非同期呼び出しのエラー処理
  4. 04子テーブルデータへのアクセス方法
  5. 05ブラウザーデバッグツールの紹介

活用シーン

  1. 01フォームフィールド未定義エラーの診断
  2. 02frappe.call の無言の失敗を修正
  3. 03子テーブル行へのアクセス問題の調査
  4. 04イベントループや保存時検証の問題を解決

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/impertio-studio/frappe_claude_skill_package --skill frappe-errors-clientscripts

インストール後、Claude Code で/frappe-errors-clientscriptsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
anti-patterns.md
examples.md
patterns.md

詳細

クライアントスクリプトのエラー — 診断と解決

関連項目:frappe-syntax-clientscripts(構文)、frappe-impl-clientscripts(ワークフロー)、frappe-errors-serverscripts(サーバー側)。


エラー診断フローチャート

ERROR IN CLIENT SCRIPT
│
├─► TypeError: Cannot read properties of undefined
│   ├─► "frm.doc.fieldname" → Field does not exist on DocType
│   ├─► "r.message.value" → Server returned null/error
│   └─► "row.fieldname" in child table → Row not fetched correctly
│
├─► frappe.call fails silently
│   ├─► Missing error callback → Add error handler
│   ├─► 403 Forbidden → Method not whitelisted (@frappe.whitelist)
│   ├─► 417 Expectation Failed → Server-side frappe.throw()
│   └─► 401 Unauthorized → Session expired or CSRF token invalid
│
├─► Uncaught (in promise) → Missing try/catch on async frappe.call
│
├─► Field appears blank after set_value → Timing issue (setup vs refresh)
│
├─► cur_frm is undefined → Using cur_frm in list/report context
│
└─► frappe.throw() does not prevent save → Used outside validate event

エラーメッセージ → 原因 → 修正方法の表

エラーメッセージ原因修正方法
TypeError: Cannot read properties of undefined (reading 'fieldname')DocTypeにフィールドが存在しない、またはdocが読み込まれていないフィールドにアクセスする前に、必ずfrm.docの存在を確認してください
TypeError: frm.set_value is not a function未定義のcur_frmショートカットを使用しているイベントハンドラーから渡されるfrmパラメーターを必ず使用してください
Uncaught (in promise)frappe.callからの非同期リジェクションが処理されていない非同期呼び出しは必ずtry/catchで囲んでください
CSRFTokenError / 403 with CSRFセッションタイムアウト後のトークン不一致CSRFを自動的に処理するfrappe.call()を必ず使用してください
Not permitted / frappe.callで403サーバーメソッドに@frappe.whitelist()がないAPIメソッドには必ず@frappe.whitelist()デコレーターを追加してください
frappe.throw() not preventing savevalidateイベント以外でfrappe.throw()を使用しているfrappe.throw()は必ずvalidate内でのみ使用してください
set_query内のfield not found: xyzfieldnameの誤字、またはフィールドが子テーブルにないDocTypeの定義を確認し、正確なfieldnameを確認してください
row.item_code is undefined子テーブルの行へのアクセス方法が誤っている — localsが同期されていない子テーブルのイベントではfrappe.get_doc(cdt, cdn)を使用してください
frm.set_value not workingフォームが完全に読み込まれる前にsetup内で呼び出されているフィールド設定のロジックをrefreshイベントに移してください
Maximum call stack exceeded循環トリガー — フィールド変更で自身のハンドラーが実行されるfrm.flagsガードを使って再帰を防いでください

重大なエラーパターン

1. cur_frmとfrm:初心者が最もよくする間違い
// ❌ WRONG — cur_frm is undefined in many contexts
frappe.ui.form.on('Sales Order', {
    customer(frm) {
        cur_frm.set_value('territory', 'Default');  // BREAKS in list view
    }
});

// ✅ CORRECT — ALWAYS use the frm parameter
frappe.ui.form.on('Sales Order', {
    customer(frm) {
        frm.set_value('territory', 'Default');
    }
});

ルール:cur_frmは絶対に使用しないでください。すべてのイベントハンドラーに渡されるfrmパラメーターを必ず使用してください。

2. Async/Await:try/catchがない場合のサイレントな失敗
// ❌ WRONG — Unhandled rejection crashes silently
frappe.ui.form.on('Sales Order', {
    async customer(frm) {
        let r = await frappe.call({
            method: 'myapp.api.get_data',
            args: { customer: frm.doc.customer }
        });
        frm.set_value('credit_limit', r.message.limit);  // r.message may be null
    }
});

// ✅ CORRECT — try/catch with null check
frappe.ui.form.on('Sales Order', {
    async customer(frm) {
        if (!frm.doc.customer) return;
        try {
            let r = await frappe.call({
                method: 'myapp.api.get_data',
                args: { customer: frm.doc.customer }
            });
            if (r.message) {
                frm.set_value('credit_limit', r.message.limit || 0);
            }
        } catch (error) {
            console.error('Customer fetch failed:', error);
            frappe.show_alert({
                message: __('Could not load customer details'),
                indicator: 'red'
            }, 5);
        }
    }
});
3. 子テーブルへのアクセス:誤ったパターン
// ❌ WRONG — frm.doc.items[0] may not reflect latest state
frappe.ui.form.on('Sales Order Item', {
    item_code(frm, cdt, cdn) {
        let row = frm.doc.items.find(r => r.name === cdn);  // fragile
        row.rate = 100;  // Does not trigger UI refresh
    }
});

// ✅ CORRECT — Use frappe.get_doc and frappe.model.set_value
frappe.ui.form.on('Sales Order Item', {
    item_code(frm, cdt, cdn) {
        let row = frappe.get_doc(cdt, cdn);
        if (!row.item_code) return;
        frappe.model.set_value(cdt, cdn, 'rate', 100);  // Triggers refresh
    }
});
4. タイミング:setupとrefresh
// ❌ WRONG — set_value in setup, form not ready
frappe.ui.form.on('Sales Order', {
    setup(frm) {
        frm.set_value('company', 'My Company');  // May not work
    }
});

// ✅ CORRECT — set_query in setup, set_value in refresh/onload
frappe.ui.form.on('Sales Order', {
    setup(frm) {
        // Filters belong in setup
        frm.set_query('customer', () => ({ filters: { disabled: 0 } }));
    },
    refresh(frm) {
        // Value changes belong in refresh (or onload for new docs)
        if (frm.is_new()) {
            frm.set_value('company', 'My Company');
        }
    }
});
5. frappe.throw()の適用範囲:validate内でのみ有効
// ❌ WRONG — throw in customer change does NOT prevent save
frappe.ui.form.on('Sales Order', {
    customer(frm) {
        if (!frm.doc.customer) {
            frappe.throw(__('Customer required'));  // Stops script, NOT save
        }
    }
});

// ✅ CORRECT — throw in validate prevents save
frappe.ui.form.on('Sales Order', {
    customer(frm) {
        if (!frm.doc.customer) {
            frappe.msgprint({ message: __('Customer required'), indicator: 'orange' });
        }
    },
    validate(frm) {
        if (!frm.doc.customer) {
            frappe.throw(__('Customer is required'));  // Prevents save
        }
    }
});
6. フラグによる再帰ガード
// ❌ WRONG — discount change triggers amount recalc, which triggers discount...
frappe.ui.form.on('Sales Order', {
    discount_percent(frm) {
        frm.set_value('grand_total', calculate(frm));  // Fires on_change loop
    }
});

// ✅ CORRECT — Use flags to break the cycle
frappe.ui.form.on('Sales Order', {
    discount_percent(frm) {
        if (frm.flags.skip_recalc) return;
        frm.flags.skip_recalc = true;
        frm.set_value('grand_total', calculate(frm));
        frm.flags.skip_recalc = false;
    }
});

デバッグツール

ツール使用方法用途
ブラウザーコンソール(F12)console.log(frm.doc)フォームの状態を確認する
console.table()console.table(frm.doc.items)子テーブルの行を表示する
JSON.parse(JSON.stringify(frm.doc))スナップショット用のディープクローンコンソールでの循環参照を回避する
frappe.boot.developer_mode開発者モードが有効か確認する条件付きデバッグログ
frappe.ui.toolbar.clear_cache()クライアントキャッシュを消去するスクリプト変更のデプロイ後
ネットワークタブ(F12)XHRリクエストをフィルターするfrappe.callのペイロードを確認する
frappe.show_alert({message: 'debug', indicator: 'blue'}, 5)UI上で視覚的にデバッグするコンソールを使わず手早くフィードバックを得る

必ず守ること/絶対にしないこと

必ず守ること
  1. frmパラメーターを使用する — cur_frmは絶対に使用しないでください[v14+]
  2. 非同期のfrappe.callをtry/catchで囲む — 未処理のリジェクションはサイレントに失敗します
  3. ユーザーに表示するすべての文字列に__()を使用する — 翻訳に必須です
  4. frappe.throw()を呼び出す前に、複数の検証エラーをまとめる
  5. イベント内で子テーブルの行にアクセスするには、frappe.get_doc(cdt, cdn)を使用する
  6. 保存を防ぐには、frappe.throw()をvalidate内でのみ使用する
  7. サーバーレスポンスのプロパティにアクセスする前に、r.messageがnullでないことを確認する
  8. 子テーブルのイベントでは、frappe.model.set_value(cdt, cdn, field, value)を使用する
絶対にしないこと
  1. alert()、confirm()、prompt()は絶対に使用しないでください — frappe.msgprint / frappe.confirm を使用します
  2. スタックトレースをユーザーに絶対に表示しないでください — コンソールに記録し、わかりやすいメッセージを表示します
  3. cur_frmは絶対に使用しないでください — 信頼性が低く、多くのコンテキストで未定義です
  4. 本番環境にconsole.logを絶対に残さないでください — frappe.boot.developer_modeの条件付きチェックを使用します
  5. 同じ関数内で.then()とawaitを絶対に混在させないでください — どちらか一方のパターンを選びます
  6. setup内でfrm.set_valueを絶対に呼び出さないでください — フォームの準備ができていません。refreshまたはonloadを使用します
  7. コールバック形式のfrappe.callでは、errorコールバックを絶対に無視しないでください

参考ファイル

ファイル内容
references/examples.md診断付きの実際のエラーシナリオ
references/anti-patterns.mdよくある誤りと修正前・修正後の例
references/patterns.md防御的なエラー処理のパターン

コメント