I am trying to create an Azure DevOps extension (in my case for Server 2020, on-premises, xml process) and for my configuration hub I keep running into "VSS.SDK.min.js:2 No handler found on any channel for message:" console errors.
I think the problem is somehow connected to the import of the sdk and/or api:
import { WorkItemFormService } from "TFS/WorkItemTracking/Services";
import * as RestClient from "TFS/WorkItemTracking/RestClient";
import { WorkItemTrackingHttpClient } from "TFS/WorkItemTracking/RestClient";
import * as ExtensionContracts from "TFS/WorkItemTracking/ExtensionContracts";
import * as SDK from "azure-devops-extension-sdk";
import { CommonServiceIds, IExtensionDataService, IExtensionDataManager } from "azure-devops-extension-api";
I then read the configuration information using
let provider = () => {
return {
onLoaded: async (workItemLoadedArgs: ExtensionContracts.IWorkItemLoadedArgs) => {
controller = new Controller(workItemLoadedArgs.id);
// ——— Debug: load and log the ConfigHub doc ———
SDK.init();
await SDK.ready();
const extCtx = SDK.getExtensionContext();
const dataService = await SDK.getService<IExtensionDataService>(CommonServiceIds.ExtensionDataService);
const token = await SDK.getAccessToken();
const dataManager = await dataService.getExtensionDataManager(extCtx.id, token);
// inside your onLoaded handler, after you’ve got dataManager:
try {
const doc: any = await dataManager.getDocument("configuration", "DefaultDoc");
// ConfigHub stores your settings under `doc.configuration`
console.log("🔧 FieldSentinel loaded ConfigHub configuration:", doc.configuration);
}
catch (e) {
console.error("🔧 FieldSentinel could not load ConfigHub configuration:", e);
}
// ——————————————————————————————————————————
},
onFieldChanged: (fieldChangedArgs: ExtensionContracts.IWorkItemFieldChangedArgs) => {
let changedValue = fieldChangedArgs.changedFields;
controller.FieldChanged(changedValue);
}
};
};
I can see the configuration being pulled correctly, so the functionality itself seems to work. But I am flooded with error messages and I am hoping that there is a simple fix for this.
Exmple handler messages (happy to upload more info somewhere, if helpful).
VSS.SDK.min.js:2 No handler found on any channel for message: {"id":5,"result":{"id":"DefaultDoc","configuration":{"version":"1","field_sentinel":{}},"__etag":9},"jsonrpc":"2.0"}
n._handleMessageReceived @ VSS.SDK.min.js:2
(anonymous) @ VSS.SDK.min.js:2Understand this error
FieldSentinel.ts:595 🔧 FieldSentinel loaded ConfigHub configuration: {version: '1', field_sentinel: {…}}
XDM.js:501 No handler found on any channel for message: {"id":7,"jsonrpc":"2.0"}
Does anybody know?
I am happy to post my manifest and webpack config, if that is of any use.
Thank you all
Alex
When the SDK logs “No handler found on any channel for message,” it means that behind the scenes every call you make to the host (whether you’re asking for a service or wiring up an event) is sent as a JSON-RPC message across a channel. If you never register a listener on that channel, the incoming messages simply drop through and trigger that console warning.
vss-extension.json
you might have something like"contributions": [
{
"id": "my-config-page",
…
}
]
but in your code, you do
VSS.register("config-page", handler);
or vice versa. Since those strings differ, the SDK never wires your handler up to the incoming messages. One easy way to avoid that is to grab the full contribution ID at runtime
const contribId = VSS.getContribution().id;
VSS.register(contribId, handler);
Just remember that getContribution().id
is the namespace-prefixed form (publisher.extension.contribution
), so your manifest has to use exactly that same full ID.
VSS.init({ explicitNotifyLoaded: true });
, you must callVSS.notifyLoadSucceeded();
after the VSS.register(...)
call. Skipping the notify step means the host never completes the handshake and the handlers never get connected.
VSS.SDK.min.js
and the new azure-devops-extension-sdk
(or calling init()
more than once) can leave you with orphaned channels. Pick one SDK, call its init()
a single time (ideally at the very start of your script), then wait for ready()
before you register handlers or grab services.Once the manifest ID and your register call match, you notify the host that you’re loaded, and you stick with a single SDK instance, those JSON-RPC warnings will disappear.