How can I get the contents of an internally linked note

What I’m trying to do

I’m trying to get the contents of all internally linked notes in a note to concatenate their text and use them as inputs for LLM summarization:

async getLinkedContents(file: TFile): Promise<string> {
        const linkedContents: string[] = [];
        
        // Get the file's metadata to extract internal links
        const metadataCache = this.app.metadataCache;
        const vault = this.app.vault;
        const fileCache = metadataCache.getFileCache(file);
               ...
}

Things I have tried

Based on my reading of the API docs, I’ve tried code like

if (fileCache?.links) {
			for (const link of fileCache.links) {
				console.log("link: ", link);
				try {
					// Internal Obsidian link
					const linkedPath = vault.getFileByPath(link.link);
					console.log("linkedPath: ", linkedPath);

This outputs “LinkedPath: null”

The problem is that the LinkCache objects returned by fileCache.link only include the text of the path, e.g. {link: 'Reading List - 2025', original: '[[Reading List - 2025]]', displayText: 'Reading List - 2025} vault.getFileByPath(‘Reading List - 2025’) returns nothing.

Gemini suggests searching over all files in vault.getFiles() myself

import { TFile } from 'obsidian';

async function resolveLinkedFile(link: any, vault: any, metadataCache: any): Promise<TFile | null> {
    const linkText = link.link;
    const normalizedLinkText = linkText.replace(/[^a-zA-Z0-9\s]/g, '').trim(); // Normalize

    const files = vault.getFiles();

    for (const file of files) {
        let normalizedFileName = file.basename.replace(/[^a-zA-Z0-9\s]/g, '').trim(); // Normalize
        if (normalizedFileName === normalizedLinkText) {
            return file;
        }

        // Check for aliases (if applicable)
        const fileCache = metadataCache.getFileCache(file);
        if (fileCache?.frontmatter?.aliases) {
            const aliases = Array.isArray(fileCache.frontmatter.aliases) ? fileCache.frontmatter.aliases : [fileCache.frontmatter.aliases];
            for (const alias of aliases) {
                const normalizedAlias = alias.replace(/[^a-zA-Z0-9\s]/g, '').trim();
                if (normalizedAlias === normalizedLinkText) {
                    return file;
                }
            }
        }
    }

    return null; // File not found
}

Before I go down that route, which seems inefficient, is there a method I’m missing that can get me the contents of another note from its internal link name? (in this case the contents of ‘Reading List - 2025’) Thanks!

What’s your context? Is it TypeScript within a plugin? Some javascript invocation within Obsidian?

This is a typescript plugin, based of the plugin template.

I tweaked the gemini-suggested code for searching the whole vault, and it works. I still want something more direct, but here’s the working code for reference.

	async resolveLinkedFile(link: any, vault: any): Promise<TFile | null> {
		const linkText = link.link;
		
		// First, try direct path resolution
		const linkedFile = vault.getAbstractFileByPath(linkText);
		if (linkedFile instanceof TFile) {
			return linkedFile;
		}
		
		// If direct path fails, try finding by name or alias
		const files = vault.getMarkdownFiles();
		for (const file of files) {
			// Check filename without extension
			const fileName = file.basename;
			if (fileName === linkText) {
				console.log("fileName: ", fileName, linkText);
				return file;
			}
			
			// Check for aliases (if applicable)
			const metadataCache = this.app.metadataCache;
			const fileCache = metadataCache.getFileCache(file);
			if (fileCache?.frontmatter?.aliases) {
				const aliases = Array.isArray(fileCache.frontmatter.aliases) ? fileCache.frontmatter.aliases : [fileCache.frontmatter.aliases];
				for (const alias of aliases) {
					if (alias === linkText) {
						return file;
					}
				}
			}
		}
		
		return null;
	}

The correct approach

const { linkPath } = splitSubpath(link.link);
const app.metadataCache.getFirstLinkpathDest(linkPath, file.path);
1 Like

A minor correction: it’s parseLinktext. Alternatively, we can also use const linkPath = getLinkpath(link.link)

Thanks for the correction, I copied this from my code and forgot I’m using my own helper

1 Like