Two related bugs prevent searchstack gsc from working on a fresh install. Both reproduce on main (current head) installed via pipx install git+https://github.com/alexpospekhov/searchstack-aeo.git@main. Companion to #4 (which covered the geo command).
Bug 1 — providers.gsc.query doesn't exist
commands/gsc_cmd.py calls gsc.query(...) in 5 places (lines 59, 104, 152, 192, 233), but providers/gsc.py only defines get_gsc_token, gsc_request, get_gsc_site_url_encoded. No query function.
Result: every searchstack gsc, searchstack gsc pages-perf, searchstack gsc devices, searchstack gsc countries, searchstack gsc trend errors with:
Error fetching GSC data: module 'searchstack.providers.gsc' has no attribute 'query'
Other commands (monitor.py, audit.py, report.py, pages.py) correctly use gsc_request directly with a constructed endpoint — that's the working pattern.
Bug 2 — get_gsc_token has no first-run OAuth bootstrap
The README claims:
This opens your browser. Sign in with the Google account that has Search Console access. Grant permissions. A token.pickle file is saved automatically.
But providers/gsc.py:20–48 only loads + refreshes an existing token.pickle. There's no InstalledAppFlow.from_client_secrets_file(...).run_local_server() call anywhere in the codebase. Searching for InstalledAppFlow, run_local_server, pickle.dump only finds the refresh-write path inside get_gsc_token, never the initial create.
Result on a clean install: even after Bug 1 is fixed, gsc_request always returns None because get_gsc_token returns None for a missing token.pickle. README promise is unfulfilled.
Repro on a clean machine
pipx install git+https://github.com/alexpospekhov/searchstack-aeo.git@main
# Configure .searchstack.toml with valid [gsc] credentials_file + site_url
searchstack gsc
# → Top Queries header prints, then AttributeError
# Even after patching Bug 1: → "GSC authentication failed" because no token.pickle exists and nothing creates it
Suggested fixes
Bug 1: add query() to providers/gsc.py
def query(
site_url: str,
start_date: str,
end_date: str,
dimensions: list[str],
config: Config,
row_limit: int = 1000,
) -> dict[str, Any]:
"""Query the GSC Search Analytics endpoint."""
encoded_site = quote(site_url, safe="")
endpoint = f"webmasters/v3/sites/{encoded_site}/searchAnalytics/query"
body = {
"startDate": start_date,
"endDate": end_date,
"dimensions": dimensions,
"rowLimit": row_limit,
}
data = gsc_request(config, endpoint, method="POST", body=body)
if data is None:
raise RuntimeError("GSC authentication failed. token.pickle missing or invalid.")
if isinstance(data, dict) and "error" in data:
raise RuntimeError(data["error"])
return data
Bug 2: add OAuth bootstrap to get_gsc_token
Prepend a missing-token branch that runs InstalledAppFlow:
if not token_path.exists():
if not creds_path.exists():
print(f"✗ GSC credentials file not found: {creds_path}")
return None
try:
from google_auth_oauthlib.flow import InstalledAppFlow
except ImportError:
print("✗ google-auth-oauthlib not installed.")
return None
SCOPES = [
"https://www.googleapis.com/auth/webmasters.readonly",
"https://www.googleapis.com/auth/webmasters",
"https://www.googleapis.com/auth/indexing",
]
print(f" No token.pickle. Starting OAuth flow. Sign in with the GSC-owning account.")
flow = InstalledAppFlow.from_client_secrets_file(str(creds_path), SCOPES)
new_creds = flow.run_local_server(port=0)
with open(token_path, "wb") as f:
pickle.dump(new_creds, f)
token_path.chmod(0o600)
return new_creds.token
google-auth-oauthlib is already in your declared dependencies (pyproject.toml).
Verified result
With both fixes applied locally, searchstack gsc on an allowed.online property returns real data:
Google Search Console -- Top Queries
Period: 2026-04-26 to 2026-05-24
No token.pickle found. Starting first-run OAuth flow.
[browser opens, user signs in + grants 3 scopes]
✓ token.pickle saved to ./token.pickle
# Query Clicks Impr CTR Pos
--- ---------------- ------- ----- ------ -----
1 allow 0 5 0.0% 56.2
2 allow online 0 1 0.0% 38.0
3 allowed 0 6 0.0% 46.3
3 queries shown.
Happy to open a PR. Tagged alongside #4 for context.
Two related bugs prevent
searchstack gscfrom working on a fresh install. Both reproduce onmain(current head) installed viapipx install git+https://github.com/alexpospekhov/searchstack-aeo.git@main. Companion to #4 (which covered thegeocommand).Bug 1 —
providers.gsc.querydoesn't existcommands/gsc_cmd.pycallsgsc.query(...)in 5 places (lines 59, 104, 152, 192, 233), butproviders/gsc.pyonly definesget_gsc_token,gsc_request,get_gsc_site_url_encoded. Noqueryfunction.Result: every
searchstack gsc,searchstack gsc pages-perf,searchstack gsc devices,searchstack gsc countries,searchstack gsc trenderrors with:Other commands (
monitor.py,audit.py,report.py,pages.py) correctly usegsc_requestdirectly with a constructed endpoint — that's the working pattern.Bug 2 —
get_gsc_tokenhas no first-run OAuth bootstrapThe README claims:
But
providers/gsc.py:20–48only loads + refreshes an existingtoken.pickle. There's noInstalledAppFlow.from_client_secrets_file(...).run_local_server()call anywhere in the codebase. Searching forInstalledAppFlow,run_local_server,pickle.dumponly finds the refresh-write path insideget_gsc_token, never the initial create.Result on a clean install: even after Bug 1 is fixed,
gsc_requestalways returnsNonebecauseget_gsc_tokenreturnsNonefor a missing token.pickle. README promise is unfulfilled.Repro on a clean machine
Suggested fixes
Bug 1: add
query()toproviders/gsc.pyBug 2: add OAuth bootstrap to
get_gsc_tokenPrepend a missing-token branch that runs InstalledAppFlow:
google-auth-oauthlibis already in your declared dependencies (pyproject.toml).Verified result
With both fixes applied locally,
searchstack gscon an allowed.online property returns real data:Happy to open a PR. Tagged alongside #4 for context.