@@ -71,7 +71,7 @@ func handleCodeIntelUpload(args []string) error {
7171 }
7272 }
7373 if err != nil {
74- return handleUploadError ( cfg . AccessToken , err )
74+ return err
7575 }
7676
7777 client := cfg .apiClient (codeintelUploadFlags .apiFlags , io .Discard )
@@ -212,94 +212,93 @@ func (e errorWithHint) Error() string {
212212 return fmt .Sprintf ("%s\n \n %s\n " , e .err , e .hint )
213213}
214214
215- // handleUploadError writes the given error to the given output. If the
216- // given output object is nil then the error will be written to standard out.
217- //
218- // This method returns the error that should be passed back up to the runner.
215+ // handleUploadError attaches actionable hints to upload errors and returns
216+ // the enriched error. Only called for actual upload failures (not flag validation).
219217func handleUploadError (accessToken string , err error ) error {
220- if errors .Is (err , upload .ErrUnauthorized ) {
221- err = attachHintsForAuthorizationError (accessToken , err )
218+ var httpErr * ErrUnexpectedStatusCode
219+ if errors .As (err , & httpErr ) && (httpErr .Code == 401 || httpErr .Code == 403 ) {
220+ isUnauthorized := httpErr .Code == 401
221+ isForbidden := httpErr .Code == 403
222+
223+ displayErr := errors .Newf ("upload failed: %s" , uploadFailureReason (httpErr ))
224+
225+ err = errorWithHint {
226+ err : displayErr ,
227+ hint : uploadHints (accessToken , isUnauthorized , isForbidden ),
228+ }
222229 }
223230
224231 if codeintelUploadFlags .ignoreUploadFailures {
225- // Report but don't return the error
226232 fmt .Println (err .Error ())
227233 return nil
228234 }
229235
230236 return err
231237}
232238
233- func attachHintsForAuthorizationError (accessToken string , originalError error ) error {
234- var actionableHints []string
235-
236- likelyTokenError := accessToken == ""
237- if _ , parseErr := accesstoken .ParsePersonalAccessToken (accessToken ); accessToken != "" && parseErr != nil {
238- likelyTokenError = true
239- actionableHints = append (actionableHints ,
240- "However, the provided access token does not match expected format; was it truncated?" ,
241- "Typically the access token looks like sgp_<40 hex chars> or sgp_<instance-id>_<40 hex chars>." )
242- }
243-
244- if likelyTokenError {
245- return errorWithHint {err : originalError , hint : strings .Join (mergeStringSlices (
246- []string {"A Sourcegraph access token must be provided via SRC_ACCESS_TOKEN for uploading SCIP data." },
247- actionableHints ,
248- []string {"For more details, see https://sourcegraph.com/docs/cli/how-tos/creating_an_access_token." },
249- ), "\n " )}
239+ // uploadHints builds two independent hint blocks: one for the Sourcegraph
240+ // access token and one for code host tokens. Both are always included.
241+ func uploadHints (accessToken string , isUnauthorized , isForbidden bool ) string {
242+ var hints []string
243+
244+ // Paragraph 1: Sourcegraph access token
245+ if isUnauthorized {
246+ if accessToken == "" {
247+ hints = append (hints , "No Sourcegraph access token was provided. Set the SRC_ACCESS_TOKEN environment variable to a valid token." )
248+ } else if _ , parseErr := accesstoken .ParsePersonalAccessToken (accessToken ); parseErr != nil {
249+ hints = append (hints , "The provided Sourcegraph access token does not match the expected format (sgp_<40 hex chars> or sgp_<instance-id>_<40 hex chars>). Was it copied incorrectly or truncated?" )
250+ } else {
251+ hints = append (hints , "The Sourcegraph access token may be invalid, expired, or you may be connecting to the wrong Sourcegraph instance." )
252+ }
253+ } else if isForbidden {
254+ hints = append (hints , "You may not have sufficient permissions on this Sourcegraph instance." )
250255 }
251256
252- needsGitHubToken := strings .HasPrefix (codeintelUploadFlags .repo , "github.com" )
253- needsGitLabToken := strings .HasPrefix (codeintelUploadFlags .repo , "gitlab.com" )
254-
255- if needsGitHubToken {
256- if codeintelUploadFlags .gitHubToken != "" {
257- actionableHints = append (actionableHints ,
258- fmt .Sprintf ("The supplied -github-token does not indicate that you have collaborator access to %s." , codeintelUploadFlags .repo ),
259- "Please check the value of the supplied token and its permissions on the code host and try again." ,
260- )
257+ // Paragraph 2: Code host token
258+ if codeintelUploadFlags .gitHubToken != "" {
259+ if isUnauthorized {
260+ hints = append (hints , "The supplied -github-token may be invalid." )
261261 } else {
262- actionableHints = append (actionableHints ,
263- fmt .Sprintf ("Please retry your request with a -github-token=XXX with collaborator access to %s." , codeintelUploadFlags .repo ),
264- "This token will be used to check with the code host that the uploading user has write access to the target repository." ,
265- )
262+ hints = append (hints , "The supplied -github-token may lack the required permissions." )
266263 }
267- } else if needsGitLabToken {
268- if codeintelUploadFlags .gitLabToken != "" {
269- actionableHints = append (actionableHints ,
270- fmt .Sprintf ("The supplied -gitlab-token does not indicate that you have write access to %s." , codeintelUploadFlags .repo ),
271- "Please check the value of the supplied token and its permissions on the code host and try again." ,
272- )
264+ } else if strings .HasPrefix (codeintelUploadFlags .repo , "github.com" ) {
265+ hints = append (hints , fmt .Sprintf ("No -github-token was provided. If this Sourcegraph instance enforces code host authentication, retry with -github-token=<token> for a token with access to %s." , codeintelUploadFlags .repo ))
266+ }
267+
268+ if codeintelUploadFlags .gitLabToken != "" {
269+ if isUnauthorized {
270+ hints = append (hints , "The supplied -gitlab-token may be invalid." )
273271 } else {
274- actionableHints = append (actionableHints ,
275- fmt .Sprintf ("Please retry your request with a -gitlab-token=XXX with write access to %s." , codeintelUploadFlags .repo ),
276- "This token will be used to check with the code host that the uploading user has write access to the target repository." ,
277- )
272+ hints = append (hints , "The supplied -gitlab-token may lack the required permissions." )
278273 }
279- } else {
280- actionableHints = append (actionableHints ,
281- "Verification is supported for the following code hosts: github.com, gitlab.com." ,
282- "Please request support for additional code host verification at https://github.com/sourcegraph/sourcegraph/issues/4967." ,
283- )
274+ } else if strings .HasPrefix (codeintelUploadFlags .repo , "gitlab.com" ) {
275+ hints = append (hints , fmt .Sprintf ("No -gitlab-token was provided. If this Sourcegraph instance enforces code host authentication, retry with -gitlab-token=<token> for a token with access to %s." , codeintelUploadFlags .repo ))
284276 }
285277
286- return errorWithHint {err : originalError , hint : strings .Join (mergeStringSlices (
287- []string {"This Sourcegraph instance has enforced auth for SCIP uploads." },
288- actionableHints ,
289- []string {"For more details, see https://docs.sourcegraph.com/cli/references/code-intel/upload." },
290- ), "\n " )}
291- }
278+ if codeintelUploadFlags .gitHubToken == "" && codeintelUploadFlags .gitLabToken == "" &&
279+ ! strings .HasPrefix (codeintelUploadFlags .repo , "github.com" ) &&
280+ ! strings .HasPrefix (codeintelUploadFlags .repo , "gitlab.com" ) {
281+ hints = append (hints , "Code host verification is supported for github.com and gitlab.com repositories." )
282+ }
292283
293- // emergencyOutput creates a default Output object writing to standard out.
294- func emergencyOutput () * output. Output {
295- return output . NewOutput ( os . Stdout , output. OutputOpts {} )
284+ hints = append ( hints , "For more details on uploading SCIP indexes, see https://sourcegraph.com/docs/cli/references/code-intel/upload." )
285+
286+ return strings . Join ( hints , " \n \n " )
296287}
297288
298- func mergeStringSlices (ss ... []string ) []string {
299- var combined []string
300- for _ , s := range ss {
301- combined = append (combined , s ... )
289+ // uploadFailureReason returns the server's response body if available, or a
290+ // generic reason derived from the HTTP status code.
291+ func uploadFailureReason (httpErr * ErrUnexpectedStatusCode ) string {
292+ if httpErr .Body != "" {
293+ return httpErr .Body
294+ }
295+ if httpErr .Code == 401 {
296+ return "unauthorized"
302297 }
298+ return "forbidden"
299+ }
303300
304- return combined
301+ // emergencyOutput creates a default Output object writing to standard out.
302+ func emergencyOutput () * output.Output {
303+ return output .NewOutput (os .Stdout , output.OutputOpts {})
305304}
0 commit comments