SharePoint REST APIでApprovers/EMail取得が失敗する原因と回避策(メール未設定ユーザー混在・Person列複数選択)

SharePoint リストの Person 列(複数選択)を REST API で取得するとき、Approvers に「メールアドレス未設定のユーザー」が混ざると Approvers/EMail の投影でエラーになることがあります。本記事では現象の整理、なぜ起きるのか、そして実務で確実に回避する設計・実装パターンを具体例つきでまとめます。

目次

起きている現象(再現条件とエラーメッセージ)

前提として、SharePoint リストに「ユーザー/グループ(Person)」列があり、列名は Approvers(複数選択) とします。アイテムの Approvers に、何らかの理由で メールアドレス(Work email / mail 属性)が空 のユーザーが含まれているケースです。

項目内容
列の種類ユーザー/グループ(Person)
列の設定複数選択(複数ユーザーを許可)
混入しているデータメールアドレス未設定(空 / null 相当)のユーザー
REST API でやりたいことApprovers/EMail を 投影(project) して 1 回で取得したい
発生するエラーCannot get value for projected field Approvers_x005f_EMail.
Graph API の挙動email が空(空文字 / null)で返り、取得自体は成功しやすい

典型的に問題が出る REST API の例は次のようなクエリです(& は HTML の都合で & 表記にしています)。

GET https://{tenant}.sharepoint.com/sites/{site}/_api/web/lists/getbytitle('ListName')/items
  ?$select=*,Approvers/EMail
  &$expand=Approvers

すると、メールアドレスが空のユーザーを 1 人でも含むアイテムに当たった瞬間に、レスポンス全体が失敗しやすくなります。ここが厄介なポイントで、アプリ側で「空なら空として扱う」以前に、API 呼び出し自体が落ちるためです。

「投影(project)」が地雷になりやすい理由

この問題の核心は、SharePoint REST API が Approvers/EMail のような形で取得するとき、内部的に「投影フィールド(projected field)」として計算した値を返そうとする点にあります。エラーメッセージの Approvers_x005f_EMail は、内部名として Approvers_EMail をエンコードしたような形(_x005f_ はアンダースコア相当)で、“Approvers から EMail を投影した結果” を意味します。

Person 列は、アイテム側に「ユーザーそのもの」が格納されているというより、参照(lookup)としてユーザー情報へリンクしています。ここで参照先ユーザーのプロパティが欠けている(今回なら EMail が空)と、投影の計算が成立せず、結果として REST API は 400/500 系で落ちることがあります。これはアプリのバグというより、REST API(OData 由来の投影の仕組み)の制約に引っかかっている整理が現実的です。

一方 Microsoft Graph は、設計として「欠損しているプロパティは null(または空)で返す」方向の挙動が多く、メールが空でもアイテム取得が成立するケースが目立ちます。つまり、同じ “email が空” でも、REST は投影計算の段階で失敗しやすく、Graph は欠損として返してくれるという差が表に出ます。

そして重要なのはここです。SharePoint REST API 側で Graph のように「欠損を空で返す」モードへ切り替えるオプションは、基本的に期待しないほうがよいという点です。挙動を変えるスイッチがあるなら一番楽ですが、実務上は「落ちない取得方法に設計を寄せる」ほうが確実です。

回避策A:Approvers/EMail を“直接投影しない”取得パターンに変える

最も堅牢で、REST API を使い続けるなら現実的なのがこの回避策です。ポイントは 2 つあります。

  • リストアイテム取得では、メールのような欠損しうるプロパティを投影しない
  • 必要なら、ユーザー ID など 落ちにくいキーで受けてから別ルートでメールを解決する

パターンA-1:まずは “ユーザーIDだけ” を安全に取る(おすすめ)

Person 列は、REST では {列内部名}Id として参照先の ID を取り出せます。複数選択なら results 配列で返ります。これを使うと、ユーザーオブジェクトのプロパティ投影を避けたまま、「誰が入っているか」を取得できます。

GET https://{tenant}.sharepoint.com/sites/{site}/_api/web/lists/getbytitle('ListName')/items
  ?$select=Id,Title,ApproversId

レスポンス例(イメージ):

{
  "d": {
    "results": [
      {
        "Id": 100,
        "Title": "申請A",
        "ApproversId": { "results": [ 23, 57 ] }
      }
    ]
  }
}

この段階では “メール” に触れていないため、メール未設定ユーザーが混ざっていても落ちにくいのが強みです。

パターンA-2:ユーザーIDからメールを解決する(単発・バッチ・キャッシュ)

次に、取得したユーザー ID を使ってユーザー情報を引きます。代表的には以下のようなエンドポイントを使います(環境・権限で選択肢は変わります)。

方法例特徴
単発で取得/_api/web/getuserbyid(23)実装が単純だが、ユーザー数が多いと N+1 になりやすい
SiteUsers を filter/_api/web/siteusers?$filter=(Id eq 23) or (Id eq 57)条件をまとめられるが、ID が多いと URL が長くなる
$batch を使う/_api/$batchHTTP 往復を抑えられ、実務では扱いやすい
キャッシュするid→メール辞書を保持同じユーザーが何度も出る業務では効果が大きい

単発取得の例(必要最小限だけ select する):

GET https://{tenant}.sharepoint.com/sites/{site}/_api/web/getuserbyid(23)
  ?$select=Id,Title,EMail,LoginName

ここで EMail が空でも、(少なくとも投影フィールドではなく)ユーザー単体のプロパティとして返るため、“空として処理する” 余地が生まれます。アプリ側では「空なら空として扱う」「UPN/ログイン名にフォールバックする」などの分岐が可能になります。

パターンA-3:アプリ側で“欠損前提”のモデルにして落とさない

メールが必須でない組織・テナントは実際にあります。外部ユーザー、特定のサービスアカウント、古いディレクトリ同期の名残など、理由は様々です。そのため Person 列を扱うアプリでは、次のような発想が安定します。

  • 承認者の主キーは「メール」ではなく ユーザー ID / LoginName とする
  • メールは通知や表示のための “補助情報” として扱い、空でも業務が止まらないようにする
  • 通知先の決定は「mail が空なら UPN を使う」「mail が空なら Teams 通知に切り替える」など 運用に寄せたフォールバックを用意する

TypeScript の簡易イメージ:

// user: SharePoint の SiteUser 相当を想定
function resolveNotificationAddress(user: any): string | null {
  const mail = (user?.EMail ?? "").trim();
  if (mail) return mail;

  // 代替案:ログイン名や UPN が取れるなら、運用に合わせて使う
  const login = (user?.LoginName ?? "").trim();
  if (login) return login;

  return null; // 最終的に通知不可として扱う(別ルートで対応)
}

ここまでを整理すると、「REST API の投影を避ける → ID を基点にユーザー情報を解決 → 欠損は欠損として扱う」という設計が、メール未設定ユーザーが混ざる現実に強い実装になります。

回避策B:Microsoft Graph API を継続利用する

要件として「メール未設定は空で返してほしい」「取得自体は必ず成功してほしい」という優先度が高いなら、Graph API を採用し続ける判断は合理的です。実際、Graph は欠損プロパティを null/空として返すことが多く、今回のような “投影フィールドの計算失敗” に巻き込まれにくい傾向があります。

ただし、Graph を採る場合も次の点は押さえておくと安全です。

観点注意点実務的な対策
メールは必ずあるとは限らないGraph でも mail は空のことがある空なら UPN、ID、displayName などへフォールバック
権限設計が別物Graph の権限・同意・アプリ登録が必要最小権限で設計し、運用手順を整備する
戻り値の形が REST と違うPerson 値の型・表現が異なるフロント/バッチの変換層を用意して吸収
“落ちない” を過信しない通信・権限・スロットリングは起こりうるリトライ、キャッシュ、部分成功の扱いを定義

Graph を使う場合でも、メールを主キーにしない、欠損前提で扱うというデータ設計は結果的に強くなります。Graph が “空で返してくれる” のは助かりますが、空である以上はアプリ側で扱いを決める必要があるためです。

補足:根本対応として「メール属性が空のユーザー」を減らす

技術的回避は重要ですが、運用上許されるなら根本対応も検討価値があります。つまり、メールが空のユーザーを減らすことです。

現場で実際に効くアプローチを、影響が小さい順に並べます。

対応狙い現場での現実性
メール未設定ユーザーの棚卸しどの種類のユーザーが空なのか把握する高(まずやるべき)
運用ルール化(承認者に使えるアカウントの定義)承認者に入れてよいユーザーを制限する中(部門間調整が必要)
ディレクトリ側の属性整備(Entra ID / AD)mail 属性や必要プロパティを統一する中〜低(管理者権限・影響範囲が大きい)
ユーザープロファイルの整備SharePoint 側参照の欠損を減らす低(環境依存・運用負荷が出やすい)

ただし、メール未設定が “例外” ではなく “仕様” に近い組織もあります。例えば、共有端末用アカウント、外部連携用の特殊アカウント、移行時の残骸などです。その場合は「属性を埋める」よりも、アプリのデータモデルを欠損耐性にするほうがトータルコストが下がることが多いです。

実務で困らないための設計パターン(おすすめ構成)

ここまでを踏まえ、Person 列(複数選択)を API 経由で扱うときの “壊れにくい” 構成例を紹介します。REST/Graph どちらでも通用する考え方です。

おすすめのデータフロー

ステップ何を取得するか落ちにくさポイント
1アイテム本体 + ApproversId高まずキー(ID)を取る。メールはここで触らない
2ユーザー情報(Id→EMail/表示名など)中〜高空を許容。N+1 を避けるためバッチ/キャッシュを検討
3通知/表示の組み立て高mail が空なら代替ルート(UPN/別チャネル/手動)を用意

「1回で全部取る」より「壊れない2段階」を選ぶ価値

一見すると、$select=Approvers/EMail&$expand=Approvers の “1回で全部” は美しいです。しかし、運用データは必ず揺れます。承認者に想定外のアカウントが入ることもあります。そのときに API が落ちる設計だと、障害対応がアプリ側に集中します。

2段階取得は面倒に見えますが、次のメリットがあります。

  • 欠損データが 1 件混ざっても、アイテム取得が止まらない
  • メール以外の属性(表示名、部署、UPN など)も後段で柔軟に扱える
  • ユーザー解決部分をキャッシュすれば、むしろパフォーマンスが安定する

よくある落とし穴と対処

「Approvers を expand してるのに、結局メールが取れない」

expand の仕方や select の仕方によっては、必要なプロパティが返らないことがあります。メールが不安定な環境では、最初から “メールを返してもらう” ことに固執せず、ApproversId を基点にユーザー解決を別処理に切り出すのが確実です。

「ユーザー解決で API 呼び出し回数が増える」

これは事実なので、以下のいずれかを組み合わせるのが現実的です。

  • アイテム群からユーザー ID をユニーク化してまとめて引く
  • SharePoint の /_api/$batch を使い、往復回数を削減する
  • id→ユーザー情報のキャッシュ(メモリ、Redis、IndexedDB など)を導入する

「メールが空だと困る(通知できない)」

技術だけで解決しない要件です。運用設計として、最低でも次のどれかを決めておくと事故が減ります。

  • 承認者に入れてよいユーザー種別を制限する
  • mail が空のときは UPN を通知先にする(可能な場合)
  • mail が空のときは Teams 通知に切り替える/管理者へエスカレーションする

Microsoft への要望として動くなら

「REST API 側も Graph のように欠損を空で返してほしい」という要望は自然です。ただ、現場の開発者が REST API の挙動に直接パッチを当てることはできません。もし組織として改善要望を出したいなら、Microsoft の開発者向けコミュニティ(Tech Community など)で同様の課題を共有し、再現条件・要望・影響範囲を整理して伝えるのが現実的なルートになります。

まとめ

SharePoint REST API で Person 列(複数選択)の Approvers/EMail を投影すると、参照先ユーザーのメールが未設定なだけで Cannot get value for projected field Approvers_x005f_EMail. のように取得自体が失敗することがあります。これをアプリ側で “空として扱う” 前に落ちるのがポイントです。

実務での最適解は、「投影でメールを取る」発想から離れ、ApproversId など落ちにくいキーを先に取り、ユーザー解決は別処理で行い、欠損は欠損として扱うことです。Graph API が要件に合うなら採用を継続するのも有効ですが、どちらにせよ “メールは欠損しうる” 前提でデータモデルを作ると運用が安定します。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次