Skip to main content

CLI outputs and troubleshooting

The CLI prints one JSON document to standard output. Complete setup before using a real mailbox.

JSON results​

A successful mutation can return:

{"status":"Moved","emailId":"immutable-message-id","targetId":"folder-id"}

A successful read can return:

{"status":"Listed","labels":[{"labelId":"category-id","labelColor":"preset4","labelName":"Important"}]}

A failure can return:

{"status":"Errored","operation":"email.move","errorReason":"The message was not found.","graphCode":"ErrorItemNotFound","graphStatusCode":404}

The error can include requestId when Microsoft supplies it. Include it in a provider incident report without sharing credentials.

Exit codes​

CodeMeaningExamples
0Successful or unchanged idempotent resultListed, Sent, Moved, Added, Removed, Unchanged
1Any failure that is not CliValidationErrorProvider/runtime failures and some input/parser errors
2A CliValidationErrorValidated missing options, invalid priority, or missing configuration on paths that use this error type

user lookup and email get, attachments, reply, reply-all, and forward use a newer command path. Missing/invalid arguments there can throw plain errors or argument-parser errors and return 1. Thus, not every input failure returns 2, and not every 1 means a provider failure.

Automation must require exit 0 before parsing success fields. For a nonzero exit, inspect the Errored result. Do not choose retries solely from code 1. A send error can still require inspection of Sent Items.

Check exit status before parsing a success field:

bun run cli -- label list --user-id person@example.com
result=$?
if [ "$result" -ne 0 ]; then
echo "Graph CLI failed with exit code $result"
fi

Status values​

StatusMeaning
ListedList retrieval finished
FoundResource retrieval finished
CreatedFolder/category created
UpdatedFolder/category updated
SentSent Items copy and internet message ID observed
ArrivedMatching recipient copy found
SeenMessage marked read
MovedMessage/folder moved
Added, RemovedCategory membership changed
UnchangedRequested idempotent category state already holds
DeletedMessage moved to Deleted Items, or folder/category deleted
PermanentlyDeletedProvider permanent-delete operation completed
ErroredOperation failed

Sent is a sender-side observation. It is not proof of recipient delivery. Arrived is a separate result.

Destructive commands​

email permanent-delete, folder delete, and label delete require --confirm. It authorizes real modification/deletion. email delete does not ask for it because that command moves the message to Deleted Items. Permanent deletion requires an eligible message in Deleted Items.

Diagnose failures​

Missing environment variable​

The JSON error names the required variable, such as CLIENT_SECRET. Correct the CLI's secure configuration for TENANT_ID, CLIENT_ID, and CLIENT_SECRET. Do not substitute another service's variable names.

401 Unauthorized​

Check an expired/invalid secret, tenant/client identity, and whether the credential belongs to that tenant.

403 Forbidden​

Check application permissions, administrator consent, Exchange application scope, protected system-folder restrictions, and retention/compliance rules. See Graph setup.

404 or ErrorItemNotFound​

Check mailbox ownership of the ID, immutable-ID format, cross-mailbox copy confusion, and permanent deletion. For a recipient copy, run email wait with the sender's internetMessageId, then use the returned emailId.

Folder name missing or ambiguous​

Names match exactly without case sensitivity. Partial names do not match. List folders:

bun run cli -- folder list --user-id person@example.com

Use the exact name or ID. Different hierarchy branches can share a display name. An ambiguity error returns matching IDs. Choose the intended ID explicitly.

Category name missing​

List categories, then use --label-id or the exact displayed name:

bun run cli -- label list --user-id person@example.com

Send confirmation timeout​

Graph accepted submission. But the tool could not observe the Sent Items copy and its internetMessageId within 30 seconds. The message can already be sent. Inspect Sent before retrying to prevent duplicates.

Arrival timeout​

Delivery can exceed --timeout-ms. Check whether recipient address and --user-id select the same mailbox. A rule or quarantine can redirect the message. A folder filter can also hide it. Increase the timeout or remove the folder filter after checking the original send.

429 Too Many Requests​

Reduce request frequency, increase --poll-interval-ms, and avoid repeated full-mailbox lists. Honor provider/SDK retry guidance.

Automation​

Store the full successful JSON before extracting a field. This keeps failure handling separate from jq:

if result=$(bun run cli -- email send \
--user-id person@example.com \
--to recipient@example.com \
--subject "Automated test" --body "Body" --priority normal); then
email_id=$(printf '%s' "$result" | jq -r '.emailId')
fi

This example sends real mail. Use an authorized test target.

Use a command whenUse a workflow when
One operation is enoughSeveral dependent steps are necessary
You inspect state manuallyYou need a repeatable scenario
A shell owns intermediate stateTypeScript types connect the steps
IDs are knownOne result supplies the next input

See programmatic workflows.