INVALID tree item
Does this issue occur when all extensions are disabled?: Yes
- VS Code Version: 1.70.0
- OS Version: Windows 11 22H2
Steps to Reproduce:
Currently I write a vscode extension with a sidebar provider. Inside the provider I set icons in front of the tree. After I updated VSCode today I got the message inside the console:
INVALID tree item, invalid iconPath {dark: 'f:\VisualStudioCode\vscode-devops-extension\resources\icons\preview-light.svg', light: 'f:\VisualStudioCode\vscode-devops-extension\resources\icons\preview-dark.svg'}
arg1:
{dark: 'f:\\VisualStudioCode\\vscode-devops-extension\\resources\\icons\\preview-light.svg', light: 'f:\\VisualStudioCode\\vscode-devops-extension\\resources\\icons\\preview-dark.svg'}
dark:
'f:\\VisualStudioCode\\vscode-devops-extension\\resources\\icons\\preview-light.svg'
light:
'f:\\VisualStudioCode\\vscode-devops-extension\\resources\\icons\\preview-dark.svg'
[[Prototype]]:
Object
Extension first-coder.manager-devops has provided an invalid tree item.
But the tree and the icons are loaded:

Here is my source of the provider:
import * as vscode from "vscode";
import * as path from "path";
import { from } from "linq-to-typescript";
import { BaseTreeProvider } from "./BaseTreeProvider";
import { BoardTreeItem } from "./BoardTreeItem";
import { ProjectTreeProvider } from "./ProjectTreeProvider";
import { CommentService, ICommentFiles } from "../Service/CommentService";
import { ITicket } from "../Models/Git/ITicket";
import { IProject } from "../Models/Git/IProject";
import { GitUtils } from "../Utils/GitUtils";
import { IBoardColumn } from "../Models/Git/IBoardColumn";
interface ICommentTicket {
filePath: string;
id: number;
childItems: BoardTreeItem[] | undefined;
}
export class BoardTreeProvider extends BaseTreeProvider {
private project: IProject | undefined;
private team: string | undefined;
private commentFiles: ICommentFiles[] | null = null;
private tickets: ITicket[] | undefined;
private boardItems: BoardTreeItem[] = [];
private isLoading = false;
constructor(
context: vscode.ExtensionContext,
projectTree: ProjectTreeProvider,
private commentService: CommentService
) {
super(context);
projectTree.onSelectedProject.subscribe(
async (project: IProject | undefined) => {
if (this.isLoading) {
vscode.window.showErrorMessage(`Konnte neues Tickets für das Projekt ${project?.name} nicht laden, da noch Tickets von vorherigen
Projekt geladen werden. Bitte versuchen Sie es nochmal, sobald die Tickets vom bisherigen Projekt geladen sind.`);
return;
}
this.isLoading = true;
this.project = project;
this.boardItems = [];
if (this.project === undefined) {
vscode.window.showErrorMessage(
"Projekt konnte nicht geladen werden. Bitte erneut versuchen.",
);
return;
}
const projectName = this.project.name;
this.team = await GitUtils.selectTeam(
await this.gitService?.getTeams(projectName),
projectName,
);
if (this.team === undefined) {
return;
}
this.refreshTree(projectName);
},
);
}
/**
* Extend the tree refresh function
*/
public async refreshTree(projectName: string) {
if (this.team !== undefined) {
this.tickets = await this.gitService?.getTickets(
projectName,
this.team,
);
this.commentFiles = this.commentService.searchFiles();
}
super.refresh();
}
/**
* Register commands to vscode
* @param context Extension context
*/
protected registerCommands(context: vscode.ExtensionContext): void {
context.subscriptions.push(
vscode.commands.registerCommand("manager-devops.refreshTickets", () => {
if (this.project?.name === undefined) {
return;
}
this.boardItems = [];
this.refreshTree(this.project.name);
})
);
}
/**
* Get the tickets and the child items to jump to the ticket in the comments
* @param tickets Tickets from gitservice api
* @returns Treeitems for the tree
*/
getTickets(tickets: ITicket[] | undefined, boardType: string): BoardTreeItem[] {
let textTickets: ICommentTicket[] = [];
let items: BoardTreeItem[] = [];
if (tickets === undefined) {
return items;
}
// Find tickets in source
if (this.commentFiles !== null) {
for (const file of this.commentFiles) {
for (const line of file.lines) {
const ticketInfo = this.commentService.parseTicketline(line.text);
let foundTicket = from(textTickets).firstOrDefault(
(x: ICommentTicket) => x.id === ticketInfo?.id,
);
if (foundTicket !== null) {
foundTicket.childItems?.push({
label: line.text.trim(),
children: undefined,
project: this.project,
iconPath: {
dark: path.join(
__filename,
"..",
"..",
"resources",
"icons",
"code-light.svg",
),
light: path.join(
__filename,
"..",
"..",
"resources",
"icons",
"code-dark.svg",
),
},
});
} else {
textTickets.push({
filePath: file.path,
id: Number(ticketInfo?.id),
childItems: [
{
command: {
command: "manager-devops-internal.openFileAtLocation",
title: "Datei öffnen",
arguments: [file.path, line.start + 1],
},
label: line.text.trim(),
children: undefined,
project: this.project,
iconPath: {
dark: path.join(
__filename,
"..",
"..",
"resources",
"icons",
"code-light.svg",
),
light: path.join(
__filename,
"..",
"..",
"resources",
"icons",
"code-dark.svg",
),
},
},
],
});
}
}
}
}
for (const ticket of tickets) {
const textTicket = from(textTickets).firstOrDefault(
(x: ICommentTicket) => x.id === ticket.id,
);
items.push({
label: "#" + ticket.id + " " + ticket.title,
id: ticket.id.toString(),
contextValue: "ticket",
team: this.team,
board: boardType,
collapsibleState:
textTicket !== null
? vscode.TreeItemCollapsibleState.Collapsed
: vscode.TreeItemCollapsibleState.None,
iconPath: {
dark: path.join(
__filename,
"..",
"..",
"resources",
"icons",
"preview-light.svg",
),
light: path.join(
__filename,
"..",
"..",
"resources",
"icons",
"preview-dark.svg",
),
},
tooltip: ticket.board,
description:
"Created on: " +
ticket.created.toLocaleDateString() +
"\nCommentcount: " +
ticket.comments,
children: textTicket?.childItems,
project: this.project,
});
}
return items;
}
/**
* Create the board column tree
* @param boardColumns columns of board from gitservice
* @param boardType The type of the board
* @returns The board tree items of columns
*/
async getBoardsColumns(
boardColumns: IBoardColumn[] | undefined,
boardType: string
): Promise<BoardTreeItem[]> {
let items: BoardTreeItem[] = [];
if (boardColumns === undefined || boardColumns.length === 0) {
return items;
}
const projectName = this.project?.name;
if (this.team === undefined || projectName === undefined) {
return items;
}
for (const boardColumn of boardColumns) {
// Need to add an s cause devops ist stupid
let boardTickets = this.tickets?.filter((t) => t.board === boardColumn.name && t.type + "s" === boardType);
items.push({
label: boardColumn.name,
id: boardColumn.id,
description: boardColumn.id,
iconPath: {
dark: path.join(
__filename,
"..",
"..",
"resources",
"icons",
"browser-light.svg",
),
light: path.join(
__filename,
"..",
"..",
"resources",
"icons",
"browser-dark.svg",
),
},
children: this.getTickets(boardTickets, boardType),
collapsibleState:
boardTickets !== undefined && boardTickets.length > 0
? vscode.TreeItemCollapsibleState.Collapsed
: vscode.TreeItemCollapsibleState.None,
project: this.project,
});
}
return items;
}
/**
* Get the tree
* @param element The elements that are already created on the tree
* @returns The whole tree
*/
async getChildren(
element?: BoardTreeItem | undefined,
): Promise<BoardTreeItem[] | undefined> {
let items: BoardTreeItem[] = [];
if (this.project === undefined) {
this.isLoading = false;
return items;
}
const projectName = this.project.name;
if (this.team === undefined) {
this.isLoading = false;
return items;
}
const boards = await this.gitService?.getBoards(projectName, this.team);
if (boards === undefined) {
this.isLoading = false;
return items;
}
if (element === undefined) {
for (const board of boards) {
const boardColumns = await this.gitService?.getColumns(
projectName,
this.team,
board.name,
);
this.boardItems.push({
label: board.name,
id: board.id.toString(),
description: board.id.toString(),
iconPath: {
dark: path.join(
__filename,
"..",
"..",
"resources",
"icons",
"table-light.svg",
),
light: path.join(
__filename,
"..",
"..",
"resources",
"icons",
"table-dark.svg",
),
},
children: await this.getBoardsColumns(boardColumns, board.name),
collapsibleState:
boardColumns !== undefined && boardColumns.length > 0
? vscode.TreeItemCollapsibleState.Collapsed
: vscode.TreeItemCollapsibleState.None,
project: this.project,
});
}
this.isLoading = false;
return this.boardItems;
}
this.isLoading = false;
return element?.children;
}
}
@lgund thank you for reporting this! I added a check for malformed tree items a few weeks ago, and it looks like I made a mistake in the check. This is a harmless log message, but it is annoying. If we have a 1.70.2 release we will include the fix there.
To verify you will need to run an extension that uses a { light: Uri; dark: Uri } for a tree item and doesn't new the tree item. An easy way to verify with this setup is the following:
- Clone the vscode-extension-samples repository and open the tree-view-sample.
- Change this line to create the
vscode.TreeItemusing{ label: ....instead ofnew: https://github.com/microsoft/vscode-extension-samples/blob/d6ed4aeb1ce1296a0c2fc882de4275da7041a54d/tree-view-sample/src/jsonOutline.ts#L118-L119 - Run the sample and open the developer tools.
- Open a json file with the running sample.
- Verify that the Json Outline view works. Verify that there are no "INVALID tree item" logs in the developer console.
Verified by @hediet