* feat(keychain): migrate API key storage to Obsidian Keychain
Replaces the legacy disk-encryption flow with Obsidian's vault-scoped
Keychain (SecretStorage) as the source of truth for API keys. Migration
is explicit and opt-in via Advanced Settings; existing vaults stay on
disk mode until the user clicks "Migrate to Obsidian Keychain".
Architecture
- `KeychainService` (singleton) wraps Obsidian's SecretStorage with a
per-vault namespace (`copilot-v{8hex}-{field}`) so multiple vaults on
one device cannot collide.
- `settingsPersistence` is the single owner of load/save:
- `loadSettingsWithKeychain()` dispatches by `_keychainOnly` flag.
- `doPersist()` writes either keychain (stripped data.json) or disk
(plaintext data.json, never new `enc_*`).
- Write queue + transaction epoch serialise every persist with
dedicated transactions (forgetAllSecrets, migrate).
- `settingsSecretTransforms` provides the pure helpers shared by both
sides (stripKeychainFields, cleanupLegacyFields, isKeychainOnly, ...).
- `encryptionService` shrinks to decrypt-only + base64/sensitive-key
helpers; new encryption writes are gone.
Migration model
- Fresh installs auto-opt-in to keychain mode; existing installs stay
disk-mode until the user clicks Migrate.
- `migrateDiskSecretsToKeychain()` is a single transaction: write
keychain -> strip data.json -> flip `_keychainOnly`. Rolls back the
keychain on partial-write or disk-save failure, and re-derives from
live settings at transaction end so concurrent edits are preserved.
- Undecryptable legacy `enc_*` values are cleared and reported via
`MigrationResult.fieldsRequiringReentry`.
Persist hardening
- Stranded vaults (keychain-only mode on a non-SecretStorage build)
preserve `_keychainOnly` and never load plaintext from disk or
silently downgrade to disk mode.
- `persistHadUndecryptableSecrets` fail-closed guard blocks disk-clear
when the last keychain save did not complete safely; a clean
disk-mode save lifts the lock so migration retry works.
- Destructive flows (clearAllVaultSecrets, forgetAllSecrets) feature-
detect `listSecrets()` and refuse before stripping disk on builds
where keychain entries cannot be enumerated.
UI
- New `MigrateConfirmModal` gates migration behind an acknowledgement
checkbox surfacing the multi-device trade-off.
- API Key Storage panel exposes active / standard / blocked /
unavailable / stranded states.
- `canClearDiskSecrets()` falls back to in-memory settings so the
Migrate / Delete-All CTAs surface immediately after a key is typed.
Testing
- New suites for keychainService, settingsPersistence,
settingsSecretTransforms, MigrateConfirmModal, password-input;
encryptionService tests rewritten for the decrypt-only surface.
- DESIGN NOTEs lock the trade-offs triaged during review (sparse
bootstrap write, rollback enc_* replay, hasEncryptionPrefix guard,
unconditional epoch bump, etc.) to prevent future review churn.
* docs(keychain): document intentional first-stage migration residue
Add a DESIGN NOTE in migrateDiskSecretsToKeychain explaining why
keychain residue from a failed first-stage persist is intentionally
left uncleaned: it stays dormant in disk-mode and self-heals on the
next successful migration or Delete All Keys.
* docs(keychain): clarify Reset Settings does not clear keychain secrets
Reset Settings rewrites data.json to defaults but does not enumerate or
remove Obsidian Keychain entries. The troubleshooting doc still promised
Reset would delete all API keys, which is false for keychain-only vaults.
- Correct the Reset warning to point users to "Delete All Keys" for
erasing keychain secrets
- Add a DESIGN NOTE on resetSettings() explaining the omission is
intentional for the first-stage migration
* style(keychain): emphasize data.json and Keychain labels in migrate modal
Add subtle background and accent coloring to the `data.json` and
`Obsidian Keychain` inline code labels so the source and destination
of the migration read more clearly.
12 KiB
Troubleshooting and FAQ
This guide covers common errors, provider-specific issues, performance problems, and frequently asked questions.
First Steps for Any Issue
Before diving into specific fixes, try these steps first:
- Check you're on the latest version of Copilot in Community Plugins
- Disable other plugins temporarily to rule out conflicts
- Enable Debug Mode in Settings → Copilot → Advanced → Debug Mode
- Open the developer console:
Cmd+Option+Ion Mac,Ctrl+Shift+Ion Windows
Common Errors
"API key not set" or "No API key configured"
Cause: The model you selected doesn't have a valid API key for its provider.
Fix:
- Go to Settings → Copilot → Basic → Set Keys
- Enter the API key for the provider your model uses
- If you're unsure which provider a model uses, check Settings → Copilot → Model — each model shows its provider
Rate Limit Errors
Cause: You've sent too many requests to the API in a short time.
Fix:
- Wait a minute and try again
- If this happens frequently during indexing, reduce Embedding Requests per Minute in QA settings (try 10–20)
- Consider upgrading your API plan with the provider
Connection Errors / Timeout
Cause: Network issue, provider outage, or the request took too long.
Fix:
- Check your internet connection
- Try again after a few seconds
- Check the provider's status page for outages
- If using a local model (Ollama/LM Studio), make sure the local server is running
"Copilot index does not exist"
Cause: You're trying to use Vault QA or semantic search but the vault hasn't been indexed yet.
Fix:
- Make sure you have an embedding model configured with a valid API key (Settings → Copilot → QA → Embedding Model)
- Run Command palette → Index (refresh) vault
- Wait for indexing to complete
"RangeError: invalid string length"
Cause: Your vault is too large for a single index partition.
Fix: Increase the number of partitions in Settings → Copilot → QA → Partitions. A good target is keeping the first index file under ~400 MB (check the .obsidian/ folder for copilot-index files and their sizes).
Response Gets Cut Off
Cause: The AI's response hit the Max Tokens limit.
Fix: Increase Max Tokens in Settings → Copilot → Model (or the per-session gear icon). Default is 6,000 tokens.
Notes Not Found in Search
Even after indexing, relevant notes aren't being returned? Try:
- Switch to Copilot Plus mode and use
@vaultfor more powerful search - Try the multilingual embedding model for non-English notes
- Review your QA inclusions/exclusions to confirm the notes aren't filtered out
- Run List all indexed files (debug command) to verify the notes are indexed
- Run Force reindex vault for a clean rebuild
"Non-markdown files are only available in Copilot Plus"
Cause: You tried to use a PDF, image, or other non-markdown file as context in a free mode.
Fix: Switch to Copilot Plus mode, or convert the file to markdown manually.
Provider-Specific Issues
Ollama
Problem: "Connection refused" or model not responding
Fix:
- Make sure Ollama is running: open a terminal and run
ollama serve - Verify the model is downloaded:
ollama list - Check that the port in Copilot settings matches (default: 11434)
- On some systems, Ollama uses
http://127.0.0.1:11434instead ofhttp://localhost:11434— try both
Azure OpenAI
Problem: Authentication errors or model not found
Fix: Azure OpenAI requires all four fields to be filled in correctly:
- API Key
- Instance Name (your Azure resource name, e.g.,
my-azure-openai) - Deployment Name (the name you gave your model deployment)
- API Version (e.g.,
2024-02-01)
Any missing or incorrect field will cause errors.
Amazon Bedrock
Problem: "Model not found" or access denied
Fix:
- Always use cross-region inference profile IDs, not bare model IDs:
- ✅
us.anthropic.claude-sonnet-4-5-20250929-v1:0 - ❌
anthropic.claude-sonnet-4-5-20250929-v1:0
- ✅
- Make sure your IAM credentials have Bedrock access permissions
- Confirm the model is available in your region
GitHub Copilot
Problem: "Token expired" or authentication fails
Fix:
- Go to Settings → Copilot → Basic → Set Keys
- Click Connect GitHub Copilot to re-authenticate via OAuth
- Make sure your GitHub Copilot subscription is active
Google Gemini
Problem: "QUOTA_EXCEEDED" or slow responses
Fix:
- Check your quota at https://console.cloud.google.com
- Try switching to the Flash model (faster, higher quota)
- Consider using Google via OpenRouter instead for a unified quota
DeepSeek
Problem: Response cuts off or streaming errors
Fix:
- DeepSeek reasoning models (deepseek-reasoner) can produce very long outputs; try increasing Max Tokens
- If you see streaming errors, check the DeepSeek status page
- Try switching between deepseek-chat and deepseek-reasoner
Performance Issues
Slow Indexing
Cause: Large vault with many notes, or low rate limit setting.
Fix:
- Check Embedding Requests per Minute — higher values speed up indexing but may cause rate limits
- Use exclusions to skip folders you don't need indexed (e.g., large archive folders)
- Use the incremental Index (refresh) vault command instead of Force Reindex when possible
- Consider Miyo (self-host) for local indexing without API rate limits
High Memory Usage
Cause: Large lexical search index or many indexed files.
Fix:
- Reduce Lexical Search RAM Limit in QA settings (default 100 MB, range 20–1000 MB)
- Add more folders to exclusions to reduce the index size
- On mobile, disable indexing altogether
UI Lag
Cause: Rendering many chat messages or a very long conversation.
Fix:
- Start a new chat — long conversations can slow down rendering
- Auto-compact will trigger automatically at 128,000 tokens to keep conversations manageable
- Lower your auto-compact threshold if you're hitting performance issues early
Settings Issues
Reset Settings to Default
If your settings get into a bad state, you can reset:
- Go to Settings → Copilot → find the reset option
- Or delete the
data.jsonfile from the plugin folder:.obsidian/plugins/copilot/data.json
⚠️ Resetting clears all your settings. API keys kept in data.json (standard storage) are removed, but keys stored in the Obsidian Keychain are not — to erase those, use Settings → Copilot → Advanced → API Key Storage → Delete All Keys. Back up your keys first.
API Key Storage
Copilot has two ways to store API keys:
- Standard storage: API keys are saved in
data.jsonin plain text. Existing vaults stay in this mode until you choose to migrate. - Obsidian Keychain: New installs use this by default. You can also switch an existing vault by going to Settings → Copilot → Advanced → API Key Storage and clicking Migrate to Obsidian Keychain. After migration,
data.jsonno longer contains your API keys.
The Obsidian Keychain is per device. If you sync your vault to another device, you may need to re-enter API keys there.
Debug Mode and Logs
For reporting bugs:
- Enable Debug Mode: Settings → Copilot → Advanced → Debug Mode
- Create a log file: Settings → Copilot → Advanced → Create Log File
- The log file opens in your vault — attach it to your bug report
Frequently Asked Questions
Is my data private? Does Copilot send my notes to the cloud?
Copilot itself doesn't store your notes on any server. However, when you send a message, the content (including any context from your notes) is sent to the AI provider you've configured (OpenAI, Anthropic, etc.) via their API. Each provider has its own privacy policy. Your notes are not sent anywhere until you actively use the chat.
The memory system stores data in your vault locally. Chat history is saved as markdown files in your vault. Nothing is stored on Copilot's servers unless you use Copilot Plus cloud features.
For maximum privacy: Google Gemini's paid API (the basis for copilot-plus-flash) does not use API request data to train its models. For complete local privacy, consider using Ollama or LM Studio with a local model — nothing leaves your machine. Self-host mode is available now for lifetime license holders — see Copilot Plus and Self-Host for details.
Can I reference a specific note in chat?
Yes — use [[Note Title]] syntax directly in your message. Copilot adds that note's content as context in the background. You can also use @-mentions. See Context and Mentions for the full list of ways to add context.
How do I make Copilot always reply in English?
Go to Settings → Copilot → Advanced → Default System Prompt, create a custom prompt, and add "Always respond in English." as an instruction. See System Prompts.
Can Copilot understand images in my notes?
Yes, but only with models that have Vision capability (shown by a vision icon in the model list). Make sure:
- You're using a vision-capable model
- Settings → Copilot → Basic → Pass markdown images to AI is enabled
Why can't Copilot read my PDF?
- Large PDFs (over 10 MB) should be converted to markdown first
- In Copilot Plus mode, use + Add context to attach a PDF — it will be converted automatically
- For large PDF collections, Projects mode is better suited (supports PDF as context natively)
Can I use Copilot offline?
With local models (Ollama or LM Studio), yes — once a model is downloaded, it runs fully offline. Cloud providers (OpenAI, Anthropic, etc.) require an internet connection.
Lexical vault search works offline. Semantic search requires an embedding model, which may also need an internet connection unless you're using a local embedding provider or Miyo.
What's the difference between Chat mode and Vault QA mode?
- Chat — General conversation. The AI only has access to your current note and anything you explicitly mention.
- Vault QA — Specifically designed for asking questions about your vault. Copilot automatically searches your notes for relevant content and includes it as context.
For most question-and-answer tasks over your vault, use Vault QA or Copilot Plus mode.
Can I use multiple providers at the same time?
Yes. You can have API keys configured for multiple providers simultaneously and switch between models from different providers at any time. You can even set a different model for quick commands vs. regular chat.
Where are my saved chats stored?
Chat conversations are saved as markdown files in your vault, in the folder copilot/copilot-conversations/ by default. You can change this folder in Settings → Copilot → Basic → Default save folder.
How do I clear the Copilot cache?
Use Command palette → Clear Copilot cache. This clears cached responses and processed files. It does not affect your chat history or the vault index.
What is the copilot/ folder in my vault?
The copilot/ folder is created by the plugin and stores:
copilot-conversations/— Saved chat historiescopilot-custom-prompts/— Your custom commandssystem-prompts/— Your custom system promptsmemory/— Saved AI memories (if enabled)
This folder is automatically excluded from vault search to avoid cluttering results.
How do I switch modes?
Click the mode selector at the top of the chat panel. Available modes:
- Chat
- Vault QA (Basic)
- Copilot Plus (requires license)
- Projects (alpha)
The AI keeps forgetting what we talked about earlier
This usually means the conversation has grown too long and older turns are being trimmed from context. Options:
- Lower Conversation Turns in Context in Model settings
- Let auto-compact handle it (it summarizes old turns automatically)
- Start a new chat and reference the previous chat file
Getting More Help
- GitHub Issues: Report bugs at https://github.com/logancyang/obsidian-copilot/issues
- Discord: Join the Copilot Discord community for help from other users
- Log file: Create a log file (Settings → Copilot → Advanced → Create Log File) and include it in bug reports
Related
- Getting Started — First-time setup
- LLM Providers — Provider-specific setup details
- Vault Search and Indexing — Index management