SharePointVault ~/gotchas/graph ⌕ Search 404 pages, error messages, scripts…Ctrl K ★ Star
M↓ README.mdM↓ todo-rejects-select.md
sharepoint-vault›gotchas›graph›todo-rejects-select.mdhistoryedit on GitHub
● GOTCHAgraph· 2 min read

Microsoft To Do rejects $select — 400 invalidRequest / RequestBroker--ParseUri

applies to: Microsoft Graph v1.0 and beta (Microsoft 365)reviewed: 2026-10-15#graph#tasks#todo#odata#troubleshooting

GET /me/todo/lists?$select=id,displayName,wellknownListName (and the same on /tasks) now fails with 400 invalidRequest, inner code RequestBroker--ParseUri. Only $select=id passes; $top and $filter still work. Drop $select from To Do calls — and when Graph returns 400, measure each call and each query parameter live before you "fix" a theory taken from the docs.

GET /me/todo/lists?$select=… (i na /tasks) teď vrací 400 invalidRequest s vnitřním kódem RequestBroker--ParseUri. Projde jen $select=id; $top a $filter fungují. U To Do $select vynech – a u Graph 400 nejdřív změř naživo jednotlivá volání a parametry, než „opravíš“ hypotézu z dokumentace.

Symptom

Creating a task (POST /me/todo/lists/{id}/tasks) fails with:

HTTP 400 · invalidRequest: Invalid request

The user has a mailbox and a To Do licence, and the code had been in production for months without changes. The error surfaces on the "create" button, so it looks like the POST body is wrong — but the failing request is the list lookup before it.

Cause

Measured on 2026-10-15 on one tenant and one user account (Graph v1.0 and beta, delegated Tasks.ReadWrite) — re-check on yours:

Request Result
GET /me/todo/lists 200
GET /me/todo/lists?$top=50 200
GET /me/todo/lists?$select=id 200
GET /me/todo/lists?$select=displayName (or any other property, or a list) 400 RequestBroker--ParseUri
GET /me/todo/lists/{id}/tasks?$filter=status ne 'completed' 200
GET /me/todo/lists/{id}/tasks?$select=id,title,status,dueDateTime 400 RequestBroker--ParseUri

The List lists reference says only that the method "supports some of the OData query parameters", without saying which — so $select is not a documented guarantee. Since the same code had worked before, treat it as a service-side change you must tolerate: don't depend on $select here.

Fix

Don't send $select to To Do; filter fields client-side.

// before: .api('/me/todo/lists').select('id,displayName,wellknownListName').top(50)
const lists = await client.api('/me/todo/lists').top(50).get();

// pick the default list; skip "Flagged Emails" in the fallback — it holds tasks made from flagged mail (Microsoft docs), not a place for a new to-do
const def = lists.value.find(l => l.wellknownListName === 'defaultList')
  ?? lists.value.find(l => l.wellknownListName !== 'flaggedEmails');

const open = await client
  .api(`/me/todo/lists/${encodeURIComponent(def.id)}/tasks`)
  .query({ $filter: "status ne 'completed'" })   // still supported
  .top(50)                                         // still supported
  .get();

Notes

  • Measure, don't guess. A first theory here was the due-date time zone (dateTimeTimeZone docs list Windows zone names; Europe/Prague is not among the examples). It was plausible, shipped — and changed nothing. Splitting the request parameter by parameter found the real cause in minutes.

  • Getting a Graph token from the browser console without an app registration: on a modern SharePoint page (the classic /_api/SP.OAuth.Token/Acquire refuses https://graph.microsoft.com with "Resource is not allowed"):

    const ml = await window.moduleLoaderPromise;
    const provider = await ml.context.aadTokenProviderFactory.getTokenProvider();
    const token = await provider.getToken('https://graph.microsoft.com');
    const r = await fetch('https://graph.microsoft.com/v1.0/me/todo/lists?$select=displayName',
      { headers: { Authorization: 'Bearer ' + token } });
    console.log(r.status, await r.json());

    The token carries the scopes granted to SPFx in your tenant and runs as the signed-in user — read-only probing is safe; don't POST test data you won't clean up.

  • Error copy: a 400 on a request URL or body is not "missing mailbox or licence". Reserve that hint for 404/403 on reads (see todo-requires-exchange-mailbox).

Was this helpful?ask or add your experience on GitHub →
Saved you an hour? A star on GitHub helps other SharePoint people find the vault.★ Star on GitHub
last reviewed: 2026-10-15last updated: 2026-10-15historyedit on GitHubview source