-
Notifications
You must be signed in to change notification settings - Fork 90
Description
Describe the bug
Using a custom connector with BC apis to patch a record I can across the following, the If-Match header kept getting an concurrency token error. Claude Opus 4.6 deemed it to be a genuine bug.
The ConnectorDataOperationExecutor._buildOperationHeaderParam method in @microsoft/power-apps does not normalize parameter names when matching input parameters to the API schema. The auto-generated service code converts hyphenated parameter names to underscores (e.g., If-Match → If_Match) because hyphens are not valid JavaScript identifiers. However, the header builder uses a direct param.name in inputParams check, so "If-Match" in { If_Match: '*' } evaluates to false and the header is silently dropped. This causes Business Central PATCH operations to fail with BadRequest_InvalidToken because the If-Match concurrency header is never sent.
Path parameter matching (_buildStandardOperationUrl) already handles this correctly via _getNormalizedParamValue(), which normalizes hyphens to underscores and does case-insensitive matching. The header builder does not use this same normalization.
Steps to Reproduce
- Create a Power Apps code app using pac code init
- Add a custom connector data source that has an operation with a hyphenated header parameter (e.g., If-Match for OData concurrency), such as a Business Central custom API
- Run pac code add-data-source to generate the service
- Observe the generated service creates a method with If_Match as the TypeScript parameter name (underscore), which is passed as a key in the params object to executeAsync
- Call the generated PATCH method with If_Match: '*' (or any ETag value)
- The PATCH call fails with BadRequest_InvalidToken / "Could not validate the client concurrency token"
Expected behavior
The SDK should normalize parameter names when matching input params to schema-defined header parameters, the same way it already does for path parameters via _getNormalizedParamValue(). The If-Match: * header should be sent in the HTTP request, and the PATCH call should succeed.
Actual behavior
The If-Match header is silently dropped because _buildOperationHeaderParam uses a direct in operator check:
// connectorDataOperationExecutor.js — _buildOperationHeaderParam (line ~258)
apiParamSpec.forEach((param) => {
if (param.in === 'header' && param.name in inputParams) { // ← no normalization
headers[param.name] = inputParams[param.name];
}
});
While path parameters use normalized matching:
// connectorDataOperationExecutor.js — _buildStandardOperationUrl (line ~426)
const value = this._getNormalizedParamValue(operationParams, param.name); // ← normalizes hyphen/underscore + case
// getNormalizedParamValue (line ~449)
getNormalizedParamValue(obj, paramName) {
const normalizedParamName = paramName.replace(/-/g, '').toLowerCase();
const foundKey = Object.keys(obj).find(
(key) => key.replace(/-/g, '').toLowerCase() === normalizedParamName
);
return foundKey !== undefined ? obj[foundKey] : undefined;
}
The fix would be to apply the same _getNormalizedParamValue logic inside _buildOperationHeaderParam instead of the raw in operator.
Environment information
Framework, build tool or relevant package used: React 19, Vite 7, TypeScript, @microsoft/power-apps SDK (Power Apps code apps)
Any connection/components: Business Central custom connector, custom API with If-Match header parameter for OData concurrency
Additional context
Workaround: Bypass the auto-generated service and call client.executeAsync() directly, passing the header parameter with its exact schema name (hyphen):
const params: Record<string, unknown> = {
tenant, environment, companyid, id,
'If-Match': '*', // Use hyphenated name so SDK header builder matches the schema
body: { sanitized },
};
client.executeAsync({ connectorOperation: { tableName, operationName: 'sanitized', parameters: params } });
This issue affects any connector operation that has hyphenated header parameters, not just If-Match. The auto-generated code will always produce underscored keys since hyphens are invalid in JS identifiers, so the mismatch is systematic.
!!! Obviously this is generated by AI, using Copilot for Github, Claude Opus 4.6 in VS Code !!!
however... it did get the patch api call working... so... hail to our AI overlords?