{"record":{"id":"35c255cda0fa7122","repo":"can1357/oh-my-pi","slug":"oauth-resource-uri-must-not-include-surrounding-wh","errorCode":null,"errorMessage":"OAuth resource URI must not include surrounding whitespace","messagePattern":"OAuth resource URI must not include surrounding whitespace","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/mcp/oauth-flow.ts","lineNumber":248,"sourceCode":"\t// registration on demand with whichever loopback URI we actually bound —\n\t// the provider issues a client_id tied to *that* URI, so the random-port\n\t// fallback remains safe for first-install DCR flows whose preferred port\n\t// happens to be occupied.\n\tconst allowPortFallback = staticClientIdFromConfig(config) === undefined;\n\treturn {\n\t\tpreferredPort: resolveCallbackPort(config.callbackPort, redirectUri),\n\t\tcallbackPath: resolveCallbackPath(config.callbackPath, redirectUri),\n\t\tcallbackHostname: resolveCallbackHostname(redirectUri),\n\t\tredirectUri,\n\t\tallowPortFallback,\n\t};\n}\n\nfunction resolveResourceUri(resource: string | undefined): string | undefined {\n\tconst trimmed = resource?.trim();\n\tif (!trimmed) return undefined;\n\tif (trimmed !== resource) {\n\t\tthrow new Error(\"OAuth resource URI must not include surrounding whitespace\");\n\t}\n\n\tconst parsed = new URL(trimmed);\n\tif (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n\t\tthrow new Error(\"OAuth resource URI must use http or https\");\n\t}\n\tif (parsed.hash) {\n\t\tthrow new Error(\"OAuth resource URI must not include a fragment\");\n\t}\n\treturn trimmed;\n}\n\ninterface ResourceIndicatorFilterOptions {\n\t/** Strip any resource URL on the same origin as the authorization server. */\n\tstripSameOriginResource?: boolean;\n}\n\n/**","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/mcp/oauth-flow.ts#L230-L266","documentation":"resolveResourceUri validates the RFC 8707 resource indicator used in the MCP OAuth flow. It trims the value to test presence, and if trimming changed the value the configured resource URI has surrounding whitespace. A whitespace-padded resource would not match the provider's registered resource indicator, breaking token audience binding, so it is rejected early.","triggerScenarios":"Configuring the OAuth resource option with leading/trailing spaces (copy-paste artifacts, env var with trailing newline, YAML/JSON padding) and then constructing the OAuth flow options (constructor) or accessing filtered/resolvedResource.","commonSituations":"Pasting the MCP server URL from documentation with a trailing space; environment variables that swallowed a newline; generated configs that concatenate strings with stray spaces.","solutions":["Remove surrounding whitespace from the configured resource URI","Trim the value at config-load time or at the source (env var export, template) before passing it to the OAuth options","Add config validation that rejects resource values differing from their trimmed form"],"exampleFix":"// before\n\"resource\": \" https://acme.example/mcp \"\n// after\n\"resource\": \"https://acme.example/mcp\"","handlingStrategy":"validation","validationCode":"const res = config.oauth?.resource;\nif (res != null && res.trim() !== res) {\n  throw new Error(`resource has surrounding whitespace: ${JSON.stringify(res)}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  startOAuthFlow(config);\n} catch (e) {\n  if (e.message.includes('resource URI must not include surrounding whitespace')) {\n    config.oauth.resource = config.oauth.resource.trim();\n    startOAuthFlow(config);\n  } else throw e;\n}","preventionTips":["Trim all OAuth config strings when loading config","Copy resource values from the MCP server config, not from a browser bar","Quote env-var values to avoid trailing newline artifacts"],"tags":["oauth","configuration","validation","whitespace"],"backgroundTag":"invalid-resource-indicator","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}