{"record":{"id":"f8ee70e8919fece9","repo":"navidrome/navidrome","slug":"missing-required-parameter-u-username","errorCode":null,"errorMessage":"missing required parameter 'u' (username)","messagePattern":"missing required parameter 'u' \\(username\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/host_subsonicapi.go","lineNumber":65,"sourceCode":"// If setJSON is true, the 'f=json' query parameter is added.\nfunc (s *subsonicAPIServiceImpl) executeRequest(ctx context.Context, uri string, setJSON bool) (*httptest.ResponseRecorder, error) {\n\tif s.router == nil {\n\t\treturn nil, fmt.Errorf(\"SubsonicAPI router not available\")\n\t}\n\n\t// Parse the input URL\n\tparsedURL, err := url.Parse(uri)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid URL format: %w\", err)\n\t}\n\n\t// Extract query parameters\n\tquery := parsedURL.Query()\n\n\t// Validate that 'u' (username) parameter is present\n\tusername := query.Get(\"u\")\n\tif username == \"\" {\n\t\treturn nil, fmt.Errorf(\"missing required parameter 'u' (username)\")\n\t}\n\n\tif err := s.checkPermissions(ctx, username); err != nil {\n\t\tlog.Warn(ctx, \"SubsonicAPI call blocked by permissions\", \"plugin\", s.pluginID, \"user\", username, err)\n\t\treturn nil, err\n\t}\n\n\t// Add required Subsonic API parameters\n\tquery.Set(\"c\", s.pluginID)         // Client name (plugin ID)\n\tquery.Set(\"v\", subsonicAPIVersion) // API version\n\tif setJSON {\n\t\tquery.Set(\"f\", \"json\") // Response format\n\t}\n\n\t// Extract the endpoint from the path\n\tendpoint := path.Base(parsedURL.Path)\n\n\t// Build the final URL with processed path and modified query parameters","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/navidrome/navidrome/blob/4ed7494a3293a9e9e647897ebfb9be327efd981b/plugins/host_subsonicapi.go#L47-L83","documentation":"The Subsonic API requires the 'u' (username) parameter on every request. executeRequest validates its presence after parsing the URI and rejects the call if it is empty. The plugin host uses this username both for permission checks and internal authentication, so it is mandatory.","triggerScenarios":"Calling subsonicAPI.Call/CallRaw with a URI whose query string lacks 'u' or has u= (empty value), e.g. '/rest/getPlaylists' or '/rest/getPlaylists?u=&v=1.16.1'.","commonSituations":"Copy-pasting an endpoint path without the auth parameters; template/config variable for the username left empty; forgetting to append the username when constructing the request programmatically.","solutions":["Append u=<username> to the query string before calling","Check that the variable supplying the username is non-empty (config not blank)","Use url.Values{\"u\": {username}, ...}.Encode() so empty values are obvious","Note other Subsonic params (v, c, t/s) may be added by the host; only u must come from you"],"exampleFix":"// before\nresp, err := api.Call(ctx, \"/rest/getPlaylists\")\n// after\nresp, err := api.Call(ctx, \"/rest/getPlaylists?u=\"+url.QueryEscape(username))","handlingStrategy":"validation","validationCode":"if username == \"\" {\n    return errors.New(\"username required for Subsonic API calls\")\n}\nuri := \"/rest/getPlaylists?u=\" + url.QueryEscape(username)","typeGuard":null,"tryCatchPattern":"if err != nil {\n    if strings.Contains(err.Error(), \"missing required parameter 'u'\") {\n        return fmt.Errorf(\"plugin config is missing the Subsonic username\")\n    }\n    return err\n}","preventionTips":["Centralize URI construction so 'u' is always appended","Fail fast at startup if the configured username is blank","Use a helper that wraps Call/CallRaw and injects required params"],"tags":["go","subsonic","missing-parameter","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"4ed7494a3293a9e9e647897ebfb9be327efd981b","analyzedAt":"2026-09-01T05:03:05.018Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}